在上一篇文章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接口
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();
}
}
測試代碼:
原始 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接口地址
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的映射文件有User.xml文件變成了UserMapper.xml,需要在mybatis的全局配置文件中重新加載映射文件
在SqlMapConfig.xml中加載UserMapper.xml
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的運行行爲。
typeAliases(別名)重點
在mapper.xml中,定義很多的statement,statement需要parameterType指定輸入參數的類型、需要resultType指定輸出結果的映射類型。
如果在指定類型時輸入類型全路徑,不方便進行開發,可以針對parameterType或resultType指定的類型定義一些別名,在mapper.xml中通過別名定義,方便開發
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映射文件名稱相同,且放在同一個目錄中。