看過之前的蛻變系列文章,相信你對mybatis有了初步的認識。但是這些還不夠,我們今天進一步來了解下mybatis的一些用法。
我們第一個程式存在很多問題,每一次操作,都需要讀取配置檔案、初始化mybati架構。這樣搞出來的程式上就一個字——渣!這讓我想起了多年以前,某個小夥伴告訴我spring的正确使用一樣,每次方法都讓spring架構重新初始化了一次。哈哈,知道你也在看的,又是一波回憶殺。
在第一個mybatis程式中,擷取SqlSession對象的方式也比較複雜,擷取SqlSession對象的操作比較複雜,由于SqlSessionFactory本身就是用來管理SqlSession對象的,應用中一般情況下有一個就足夠了!我們做一個小優化——使用單利模式來封裝下,讓一個SqlSessionFactory對象去管理SqlSession對象。
1.建立一個MyBatisUtil工具類:
package com.pz.route.util; import java.io.IOException;import java.io.InputStream; import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder; public class MyBatisUtil { private static SqlSessionFactory sqlSessionFactory; static{ //讀取主配置檔案 InputStream input = null; if (sqlSessionFactory == null) { try { input = Resources.getResourceAsStream("mybatis.xml"); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } synchronized (MyBatisUtil.class) { if (sqlSessionFactory == null){ sqlSessionFactory = new SqlSessionFactoryBuilder().build(input); } } } } publicstatic SqlSession getSqlSession() { returnsqlSessionFactory.openSession(); } }
上面的例子使用了靜态代碼塊的方式建立了一個SqlSessionFactory對象,将SqlSession交給SqlSessionFactory進行管理。 2. 修改TravelRouteDaoImpl類:
package com.pz.route.dao.impl; import org.apache.ibatis.session.SqlSession; import com.pz.route.dao.TravelRouteDao;import com.pz.route.domain.TravelRoute;import com.pz.route.util.MyBatisUtil; public class TravelRouteDaoImpl implements TravelRouteDao { private SqlSession sqlSession; @Override public void add(TravelRoute travelRoute) { try { sqlSession = MyBatisUtil.getSqlSession(); //新增資料操作 sqlSession.insert("com.pz.route.dao.TravelRouteDao.add", travelRoute); //送出SqlSession sqlSession.commit(); //無需再做關閉,SqlSessionFactory會自動關閉sqlSession } catch (Exception e) { e.printStackTrace(); } } }
SqlSession繼承了AutoCloseable接口,SqlSessionFactory會自動關閉SqlSession。
資料庫的一些資訊最好不要寫死在xml裡,大家基本上都是通過配置的方式讀取資料庫相關資訊。在resources目錄下建立db.properties配置檔案,裡面填寫下面内容:
jdbc.driverClassName=com.mysql.jdbc.Driverjdbc.url=jdbc:mysql://127.0.0.1:3306/route?characterEncoding=utf8jdbc.username=rootjdbc.password=123456
修改mybatis.xml檔案,使用将資料庫連接配接資訊修改為使用配置檔案的方式:
<?xml version="1.0"encoding="UTF-8" ?>/span> PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration> <properties resource="db.properties"/> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver"value="${jdbc.driverClassName}"/> <property name="url"value="${jdbc.url}"/> <property name="username"value="${jdbc.username}"/> <property name="password"value="${jdbc.password}"/> dataSource> environment> environments> <mappers> <mapper resource="mapper/TravelRouteMapper.xml"/> mappers>configuration>
我們建立了Mybatis.xml作為使用Mytatis的主配置檔案,那麼這個主配置檔案應該幹些什麼事情呢?
1.配置外部配置檔案,友善程式使用(比如配置db.properties)
2.設定全局Domain類JavaBean的别名(每次使用類的名稱空間寫起來太麻煩了)
3.配置運作環境
4.配置mapper檔案(有哪些資料操作使用了mybatis需要要告訴架構)
5. 設定全局的資料庫操作配置,比如事務逾時時間等等資訊
我們看看mybatis.xml中的内容:
<properties resource="db.properties"/
這樣配置以後,mybatis就可以從db.properties中擷取資料庫資訊了。
我們看看這個配置我們在mapper檔案中的寫法:
<insert id="add"parameterType="com.pz.route.domain.TravelRoute">
在mapper中的parameterType需要使用類的名稱空間:包名+類名的方式。每次這樣寫比較麻煩,也比較容易寫錯。MyBatis提供了标簽用以定義别名。
<typeAliases> <typeAlias type="com.pz.route.domain.TravelRoute"alias="TravelRoute"/> typeAliases>
這樣子,在mapper中就可以直接使用TravelRoute來代替之前的 com.pz.route.domain.TravelRoute。
一個項目上線,需要經曆多個階段,一般來講一個項目需要經過開發,測試,上線的生命周期。那麼不可避免的,一個項目會有多個運作環境。
比如在開發階段,應用往往運作在本地,測試階段,會運作在測試伺服器,到了應用上線之後,應用自然運作在生産機器上。在不同的環境下,資料庫也會有多個,比如開發環境在A庫,測試時在B庫,項目上線以後在生産庫C,那麼為了支援不同的環境,mybatis提供了<environments>标簽來支援多個環境的問題。
<environments default="development"> <environment id="development"> <transactionManager type="JDBC" /> <dataSource type="POOLED"> <property name="driver"value="${jdbc.driverClassName}" /> <property name="url"value="${jdbc.url}" /> <property name="username"value="${jdbc.username}" /> <property name="password"value="${jdbc.password}" /> dataSource> environment> <environment id="test"> <transactionManager type="JDBC" /> <dataSource type="POOLED"> <property name="driver"value="${jdbc.test.driver}" /> <property name="url"value="${jdbc.test.url}" /> <property name="username"value="${jdbc.test.user}" /> <property name="password"value="${jdbc.test.password}" /> dataSource> environment> environments>
大家可以看到再environments标簽中有兩個environment子标簽,配置了兩個資料庫資訊environments标簽中的default屬性,用于選擇到底使用的是哪個環境。
transactionManager标簽用于設定事務管理器用于MyBatis的事務管理。MyBatis 支援兩種事務管理器類型:
JDBC需要再程式中使用通過SqlSession對象的commit()方法送出,通過rollback()方法復原,預設情況下是需要手動送出的。
MANAGED由Mybatis來管理事務的整個事務的生命周期,預設情況下,每次操作都會關閉資料庫連接配接。
dataSource标簽主要用于配置應用的資料源連接配接方式和資料庫連接配接資訊,使用type屬性來設定資料源類型:
UNPOOLED表示不使用資料庫連接配接池,每次資料庫操作Mybatis都需要建立一個Connection對象。
POOLED使用Mybatis提供的資料庫連接配接池。
JNDI使用JNDI資料源(JNDI的方式一般是配置在應用伺服器中)
Mapper 标簽的作用就是通知Mybatis使用哪些mapper檔案,一共有四種寫法:
第一種:使用mapper檔案的相對路徑,這個路徑是相對于classpath而言的。
<mapper resource="mapper/TravelRouteMapper.xml" />
第二種:使用url
<mapper url="D://xxx/xxxMapper.xml" />
第三種:使用類的名稱空間,使用這種方式,需要滿足以下幾個規約:
1.Mapper檔案名要與 Dao 接口名相同
2.Mapper檔案要與接口在同一包中
3.Mapper檔案中的 namespace 屬性值為 Dao 接口的類的名稱空間
<mapper class="com.pz.route.dao.TravelRouteDao" />
第四種:使用mybatis提供的動态代理實作(無需自己實作xxxDaoImpl)
使用這種方式,需要滿足以下幾個規約:
(1)dao 使用 mapper 的動态代理實作(後面再将)
(2)Mapper檔案名與 Dao 接口名相同
(3)Mapper檔案與接口放在同一包中
(4)Mapper檔案中的 namespace 屬性值為 Dao接口的類的名稱空間
<mapper package="com.pz.route.dao" />
我們編寫的第一個MyBatis程式,實作了一個向route資料庫travel_route表中新增一條資料的功能,有一些比較值得注意的地方大家一起來看下:
<insert id="add"parameterType="com.pz.route.domain.TravelRoute"> INSERT INTO travel_route (travel_route_name,travel_route_price,travel_route_introduce,travel_route_flag,travel_route_date,isThemeTour,travel_route_count,travel_route_cid,travel_route_image,travel_route_seller_id) values(#{travelRouteName},#{travelRoutePrice},#{travelRouteIntroduce},#{travelRouteFlag},#{travelRouteDate},#{isThemeTour},#{travelRouteCount},#{travelRouteCid},#{travelRouteImage},#{travelRouteSellerId}) insert>
id:需要執行的SQL語句的唯一辨別,實際上它mybatix被識别的方式是namespace+id,一個id可被用代表一段需要執行的sql語句,在一個mapper檔案中對id值的要求是不能重複出現的。
parameterType:sql語句中參數的類型,實際上MyBatis利用反射機制感覺dao接口的參數類型,不寫這個配置也是可以的
#{ }:這個符号表示參數,支援el表達式,我們通過JavaBean的方式做參數傳遞,花括号裡的值為JavaBean的屬性,如果屬性也是對象,支援對象名.方法名的形式。
Mybatis事務送出,我們看TravelRouteDaoImpl:
try { sqlSession = MyBatisUtil.getSqlSession(); //新增資料操作 sqlSession.insert("com.pz.route.dao.TravelRouteDao.add", travelRoute); //送出SqlSession sqlSession.commit(); //無需再做關閉,SqlSessionFactory會自動關閉sqlSession } catch (Exception e) { e.printStackTrace(); }
預設情況下Mybatis的事務是不會自動送出的,如果想自動送出事務,可以在MyBatisUtil中修改openSession的方法,傳入參數true。如果不傳入任何參數或者傳入false,mybatis無法自動送出事務。
public static SqlSession getSqlSession() { return sqlSessionFactory.openSession(true); }
如果不送出事務,資料是不會持久化到資料庫的。
travel_route表,我們設定的主鍵是自增長類型,是以在編寫SQL的時候,我們不用寫入主鍵travel_route_id,如果我們想在插入資料後擷取主鍵我們可以使用下面的方式:
<selectKey resultType="Long"keyProperty="travelRouteId" order="AFTER"> SELECT @@identity selectKey>
這樣子在mybatis完成insert操作之後後,會将目前寫入資料的ID查詢出來,重寫Travle的toString方法,再編寫一段程式看下效果:
@Test public void testAddTravelRoute(){ TravelRouteDao travelRouteDao = new TravelRouteDaoImpl(); TravelRoute travelRoute = new TravelRoute(); travelRoute.setTravelRouteName("新增線路2"); travelRoute.setTravelRoutePrice(999d); travelRoute.setTravelRouteDate("2019-10-26"); travelRoute.setTravelRouteFlag(1); travelRoute.setIsThemeTour("1"); travelRoute.setTravelRouteCount(0); travelRoute.setTravelRouteCid(1); travelRoute.setTravelRouteIntroduce("雙導遊服務,免收服務小費,周全照顧貼心服務随心出遊!品嘗越南特色國寶美食,更新一餐越式炸雞火鍋宴!"); travelRoute.setTravelRouteImage("img/product/small/m3db4d2277b5df3d98597f79082ef92d6d.jpg"); travelRoute.setTravelRouteSellerId(1L); System.out.println("before insert==="); System.out.println(travelRoute); travelRouteDao.add(travelRoute); System.out.println("after insert==="); System.out.println(travelRoute); }
我們再在TravelRouteDaoImpl中commit列印增加兩行列印代碼
@Override public void add(TravelRoute travelRoute) { try { sqlSession = MyBatisUtil.getSqlSession(); //新增資料操作 sqlSession.insert("com.pz.route.dao.TravelRouteDao.add", travelRoute); System.out.println("before commit==="); System.out.println(travelRoute); //送出SqlSession sqlSession.commit(); //無需再做關閉,SqlSessionFactory會自動關閉sqlSession } catch (Exception e) { e.printStackTrace(); } }
運作程式後,我們可以清晰地看到,在commit之前,id已經有值了,實際上,當insert語句執行後,commit之前,主鍵id已經傳回了資料,這個事務不管是commit或者是rollback,travel_route的主鍵已經使用掉一個了,再做insert操作mysql隻會配置設定一個新的主鍵。由此我們得出一個結論:資料庫主鍵的配置設定和是否送出無關,隻要執行了insert操作,mysql就會配置設定一個主鍵。