天天看點

MyBatis學習(二)

    在上一篇文章MyBatis學習(一)中我們對mybatis架構有了一個大緻的認識也算基本上入門了,并且完成了“根據使用者id(主鍵)查詢使用者資訊”這個案例。在這篇文章中我們繼續學習mybits中更多的進階用法。

    首先我們接着完成第三個案例“添加使用者”

在User.xml檔案中編寫添加使用者的sql語句:

<!-- 添加使用者 -->
	<insert id="insertUser" parameterType="com.travelsky.mybatis.po.User">
		INSERT INTO USER (username,sex,address) VALUE(#{username},#{sex},#{address});
	</insert>
           

測試代碼:

// 添加使用者資訊
	@Test
	public void testInsert() {
		// 資料庫會話執行個體
		SqlSession sqlSession = null;
		try {
			// 建立資料庫會話執行個體sqlSession
			sqlSession = sqlSessionFactory.openSession();
			// 添加使用者資訊
			User user = new User();
			user.setUsername("張小明");
			user.setAddress("河南鄭州");
			user.setSex("1");
			user.setPrice(1999.9f);
			sqlSession.insert("test.insertUser", user);
			//送出事務
			sqlSession.commit();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (sqlSession != null) {
				sqlSession.close();
			}
		}
	}
           

至此,通過以上三個案例的學習可以簡單總結出上篇文章中的幾個問題:

Mybatis解決jdbc程式設計的問題

1、 資料庫連結建立、釋放頻繁造成系統資源浪費進而影響系統性能,如果使用資料庫連結池可解決此問題。

解決:在SqlMapConfig.xml中配置資料連結池,使用連接配接池管理資料庫連結。

2、 Sql語句寫在代碼中造成代碼不易維護,實際應用sql變化的可能較大,sql變動需要改變java代碼。

解決:将Sql語句配置在XXXXmapper.xml檔案中與java代碼分離。

3、 向sql語句傳參數麻煩,因為sql語句的where條件不一定,可能多也可能少,占位符需要和參數一一對應。

解決:Mybatis自動将java對象映射至sql語句,通過statement中的parameterType定義輸入參數的類型。

4、 對結果集解析麻煩,sql變化導緻解析代碼變化,且解析前需要周遊,如果能将資料庫記錄封裝成pojo對象解析比較友善。

解決:Mybatis自動将sql執行結果映射至java對象,通過statement中的resultType定義輸出結果的類型。

同時也對mybatis的知識點進行小結:

#{ }與${ }的差別:

#{}表示一個占位符号,#{}接收輸入參數,類型可以是簡單類型,pojo、hashmap。

如果接收簡單類型,#{}中可以寫成value或其它名稱,我一般習慣寫資料庫字段名。

#{}接收pojo對象值,通過OGNL讀取對象中的屬性值,通過屬性.屬性.屬性...的方式擷取對象屬性值。

${}表示一個拼接符号,表示拼接sql串,将接收到的參數的内容不加任何修飾拼接在sql中,會引用sql注入,是以不建議使用${}。

${}接收輸入參數,類型可以是簡單類型,pojo、hashmap。

如果接收簡單類型,${}中隻能寫成value。

${}接收pojo對象值,通過OGNL讀取對象中的屬性值,通過屬性.屬性.屬性...的方式擷取對象屬性值。

selectOne和selectList

selectOne表示查詢出一條記錄進行映射。如果使用selectOne可以實作使用selectList也可以實作(list中隻有一個對象)。

selectList表示查詢出一個清單(多條記錄)進行映射。如果使用selectList查詢多條記錄,不能使用selectOne。

如果使用selectOne報錯:

org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 4

mybatis和hibernate本質差別以及應用場景

hibernate:是一個标準ORM架構(對象關系映射)。入門門檻較高的,不需要程式寫sql,sql語句自動生成了。

對sql語句進行優化、修改比較困難的。

應用場景:

适用與需求變化不多的中小型項目,比如:背景管理系統,ERP、ORM、OA。

mybatis:專注是sql本身,需要程式員自己編寫sql語句,sql修改、優化比較友善。mybatis是一個不完全 的ORM架構,雖然程式員自己寫sql,mybatis 也可以實作映射(輸入映射、輸出映射)。

應用場景:

适用與需求變化較多的項目,比如:網際網路項目。

企業進行技術選型,以低成本 高回報作為技術選型的原則,根據項目組的技術力量進行選擇。

MyBatis開發的兩種方式:

使用Mybatis開發Dao,通常有兩個方法,即原始Dao開發方法和Mapper接口開發方法。

在介紹兩種開發方法之前,先介紹下mybatis中重要的API:

1,SqlSessionFactoryBuilder

SqlSessionFactoryBuilder用于建立SqlSessionFacoty,SqlSessionFacoty一旦建立完成就不需要SqlSessionFactoryBuilder了,因為SqlSession是通過SqlSessionFactory生産,是以可以将SqlSessionFactoryBuilder當成一個工具類使用,最佳使用範圍是方法範圍即方法體内局部變量。

示例代碼:

String resource = "SqlMapConfig.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
           

2,SqlSessionFactory

SqlSessionFactory是一個接口,接口中定義了openSession的不同重載方法,SqlSessionFactory的最佳使用範圍是整個應用運作期間,一旦建立後可以重複使用,通常以單例模式管理SqlSessionFactory。将來mybatis和spring整合後,使用單例模式管理sqlSessionFactory。

3,SqlSession

SqlSession是一個面向使用者的接口, sqlSession中定義了資料庫操作,預設使用DefaultSqlSession實作類。

執行過程如下:

1、 加載資料源等配置資訊

Environment environment = configuration.getEnvironment();

2、 建立資料庫連結

3、 建立事務對象

4、 建立Executor,SqlSession所有操作都是通過Executor完成,mybatis源碼如下:

if (ExecutorType.BATCH == executorType) {
      executor = newBatchExecutor(this, transaction);
    } elseif (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
if (cacheEnabled) {
      executor = new CachingExecutor(executor, autoCommit);
    }
           

5、 SqlSession的實作類即DefaultSqlSession,此對象中對操作資料庫實質上用的是Executor

結論:

SqlSession中提供了很多操作資料庫的方法:如:selectOne(傳回單個對象)、selectList(傳回單個或多個對象)。每個線程都應該有它自己的SqlSession執行個體。SqlSession的執行個體不能共享使用,它也是線程不安全的。是以最佳的範圍是請求或方法範圍。絕對不能将SqlSession執行個體的引用放在一個類的靜态字段或執行個體字段中。

SqlSession最佳應用場合在方法體内,定義成局部變量使用。

打開一個 SqlSession;使用完畢就要關閉它。通常把這個關閉操作放到 finally 塊中以確定每次都能執行關閉。如下:

SqlSession session = sqlSessionFactory.openSession();
	try {
 		 // do work
	} finally {
  		session.close();
           

原始Dao開發方法(需要手動編寫Dao接口和Dao實作類)

思路:

需要向dao實作類中注入SqlSessionFactory,在方法體内通過SqlSessionFactory建立SqlSession

dao接口

MyBatis學習(二)

dao接口對應的實作類

public class UserDaoImpl implements UserDao {

	// 需要向dao實作類中注入SqlSessionFactory
	// 這裡通過構造方法注入
	private SqlSessionFactory sqlSessionFactory;

	public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
		this.sqlSessionFactory = sqlSessionFactory;
	}

	@Override
	public User findUserById(int id) throws Exception {
		SqlSession sqlSession = sqlSessionFactory.openSession();

		User user = sqlSession.selectOne("test.findUserById", id);

		// 釋放資源
		sqlSession.close();

		return user;

	}

	@Override
	public void insertUser(User user) throws Exception {
		SqlSession sqlSession = sqlSessionFactory.openSession();

		//執行插入操作
		sqlSession.insert("test.insertUser", user);

		// 送出事務
		sqlSession.commit();

		// 釋放資源
		sqlSession.close();

	}

	@Override
	public void deleteUser(int id) throws Exception {
		SqlSession sqlSession = sqlSessionFactory.openSession();

		//執行插入操作
		sqlSession.delete("test.deleteUser", id);

		// 送出事務
		sqlSession.commit();

		// 釋放資源
		sqlSession.close();

	}

}
           

測試代碼:

MyBatis學習(二)

原始 dao開發問題總結

1、dao接口實作類方法中存在大量模闆方法,設想能否将這些代碼提取出來,大大減輕程式員的工作量。

2、調用sqlsession方法時将statement的id寫死了

3、調用sqlsession方法時傳入的變量,由于sqlsession方法使用泛型,即使變量類型傳入錯誤,在編譯階段也不報錯,不利于程式員開發。

Mapper動态代理方式(隻需要開發mapper接口即可,相當于dao接口)

Mapper接口開發方法隻需要程式員編寫Mapper接口(相當于Dao接口),由Mybatis架構根據接口定義建立接口的動态代理對象,代理對象的方法體同上邊Dao接口實作類方法。

開發者需要編寫mapper.xml映射檔案

編寫mapper接口需要遵循以下開發規範

1、在mapper.xml中namespace等于mapper接口位址

MyBatis學習(二)

2、mapper.java接口中的方法名和mapper.xml中statement的id一緻

3、mapper.java接口中的方法輸入參數類型和mapper.xml中statement的parameterType指定的類型一緻。

4、mapper.java接口中的方法傳回值類型和mapper.xml中statement的resultType指定的類型一緻。

UserMapper.xml和UserMapper.java

MyBatis學習(二)
MyBatis學習(二)

由于mybatis的映射檔案有User.xml檔案變成了UserMapper.xml,需要在mybatis的全局配置檔案中重新加載映射檔案

在SqlMapConfig.xml中加載UserMapper.xml

MyBatis學習(二)

mapper接口開發方式的測試代碼:

Public class UserMapperTest extends TestCase {

	private SqlSessionFactory sqlSessionFactory;
	
	protected void setUp() throws Exception {
		//mybatis配置檔案
		String resource = "sqlMapConfig.xml";
		InputStream inputStream = Resources.getResourceAsStream(resource);
		//使用SqlSessionFactoryBuilder建立sessionFactory
		sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
	}

	
	Public void testFindUserById() throws Exception {
		//擷取session
		SqlSession session = sqlSessionFactory.openSession();
		//擷取mapper接口的代理對象
		UserMapper userMapper = session.getMapper(UserMapper.class);
		//調用代理對象方法
		User user = userMapper.findUserById(1);
		System.out.println(user);
		//關閉session
		session.close();
		
	}
	@Test
	public void testFindUserByUsername() throws Exception {
		SqlSession sqlSession = sqlSessionFactory.openSession();
		UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
		List<User> list = userMapper.findUserByUsername("張");
		System.out.println(list.size());

	}
Public void testInsertUser() throws Exception {
		//擷取session
		SqlSession session = sqlSessionFactory.openSession();
		//擷取mapper接口的代理對象
		UserMapper userMapper = session.getMapper(UserMapper.class);
		//要添加的資料
		User user = new User();
		user.setUsername("張三");
		user.setBirthday(new Date());
		user.setSex("1");
		user.setAddress("北京市");
		//通過mapper接口添加使用者
		userMapper.insertUser(user);
		//送出
		session.commit();
		//關閉session
		session.close();
	}
	

}
           

哪一種更合适開發?

mybatis官方推薦使用mapper代理方法開發mapper接口,程式員不用編寫mapper接口實作類,使用mapper代理方法時,輸入參數可以使用pojo包裝對象或map對象,保證dao的通用性。

細心的朋友可能會發現,由于parameterType中參數隻有一個,是以mapper接口方法的參數也隻能有一個,這樣子會不會不利于系統的擴充呢?關于這個問題,我們将在下篇文章中回答。

下面總結一下關于mybatis全局配置檔案SqlMapConfig.xml檔案的配置及使用方式:

properties屬性

 需求:

将資料庫連接配接參數單獨配置在db.properties中,隻需要在SqlMapConfig.xml中加載db.properties的屬性值。

在SqlMapConfig.xml中就不需要對資料庫連接配接參數寫死。

将資料庫連接配接參數隻配置在db.properties中,原因:友善對參數進行統一管理,其它xml可以引用該db.properties。

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=mysql
           

在sqlMapConfig.xml加載屬性檔案:

<properties resource="db.properties"/>
	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC"/>
			<dataSource type="POOLED">
				<property name="driver" value="${jdbc.driver}"/>
				<property name="url" value="${jdbc.url}"/>
				<property name="username" value="${jdbc.username}"/>
				<property name="password" value="${jdbc.password}"/>
			</dataSource>
		</environment>
	</environments>
           

properties特性:

注意: MyBatis 将按照下面的順序來加載屬性:

1, 在 properties 元素體内定義的屬性首先被讀取。

2, 然後會讀取properties 元素中resource或 url 加載的屬性,它會覆寫已讀取的同名屬性。

3,最後讀取parameterType傳遞的屬性,它會覆寫已讀取的同名屬性。

建議:

不要在properties元素體内添加任何屬性值,隻将屬性值定義在properties檔案中。

在properties檔案中定義屬性名要有一定的特殊性,如:XXXXX.XXXXX.XXXX

settings全局參數配置

mybatis架構在運作時可以調整一些運作參數。

比如:開啟二級緩存、開啟延遲加載。。

全局參數将會影響mybatis的運作行為。

MyBatis學習(二)
MyBatis學習(二)
MyBatis學習(二)

typeAliases(别名)重點

在mapper.xml中,定義很多的statement,statement需要parameterType指定輸入參數的類型、需要resultType指定輸出結果的映射類型。

如果在指定類型時輸入類型全路徑,不友善進行開發,可以針對parameterType或resultType指定的類型定義一些别名,在mapper.xml中通過别名定義,友善開發

mybatis預設支援的别名清單:

MyBatis學習(二)

自定義别名:

在SqlMapConfig.xml中配置

<typeAliases>
	<!-- 單個别名定義 -->
	<typeAlias alias="user" type="cn.travelsky.mybatis.po.User"/>
	<!-- 批量别名定義,掃描整個包下的類,别名為類名(首字母大寫或小寫都可以) -->
	<package name="cn.travelsky.mybatis.po"/>
	<package name="其它包"/>
</typeAliases>
           

Mapper.xml映射檔案中定義了操作資料庫的sql,每個sql是一個statement,映射檔案是mybatis的核心。

mapper映射配置

1,通過resource加載單個映射檔案

<mapper resource=" " />

使用相對于類路徑的資源

如:<mapper resource="sqlmap/User.xml" />

2,通過mapper接口加載單個mapper

<mapper class=" " />

使用mapper接口類路徑 如:<mapper class="cn.travelsky.mybatis.mapper.UserMapper"/> 注意:此種方法要求mapper接口名稱和mapper映射檔案名稱相同,且放在同一個目錄中。

3,批量加載mapper(開發中推薦使用)

<package name=""/>
注冊指定包下的所有mapper接口
如:<package name="cn.travelsky.mybatis.mapper"/>
注意:此種方法要求mapper接口名稱和mapper映射檔案名稱相同,且放在同一個目錄中。
           

繼續閱讀