mybatis在項目中的使用
一、mybatis開發dao層
使用MyBatis開發Dao,通常有兩個方法,即原始Dao開發方法和Mapper動態代理開發方法。
1.1 SqlSession的使用範圍
- SqlSession中封裝了對數據庫的操作,如:查詢、插入、更新、刪除等。
- SqlSession通過SqlSessionFactory創建。
- SqlSessionFactory是通過SqlSessionFactoryBuilder進行創建。
1.1.1 SqlSessionFactoryBuilder
SqlSessionFactoryBuilder用於創建SqlSessionFacoty,SqlSessionFacoty一旦創建完成就不需要SqlSessionFactoryBuilder了,因爲SqlSession是通過SqlSessionFactory創建的。所以可以將SqlSessionFactoryBuilder當成一個工具類使用,最佳使用範圍是方法範圍即方法體內局部變量。
1.1.2 SqlSessionFactory
SqlSessionFactory是一個接口,接口中定義了openSession的不同重載方法,SqlSessionFactory的最佳使用範圍是整個應用運行期間,一旦創建後可以重複使用,通常以單例模式管理SqlSessionFactory。
1.1.3 SqlSession
SqlSession是一個面向用戶的接口,sqlSession中定義了數據庫操作方法。
每個線程都應該有它自己的SqlSession實例。SqlSession的實例不能共享使用,它也是線程不安全的。因此最佳的範圍是請求或方法範圍。絕對不能將SqlSession實例的引用放在一個類的靜態字段或實例字段中。
打開一個 SqlSession;使用完畢就要關閉它。通常把這個關閉操作放到 finally 塊中以確保每次都能執行關閉。如下:
SqlSession session = sqlSessionFactory.openSession();
try {
// do work
} finally {
session.close();
}
1.2 原始Dao開發方式
原始Dao開發方法需要程序員編寫Dao接口和Dao實現類
1.2.1 編寫映射文件
採用mybatis的基礎入門裏面的映射文件mybatis的基礎入門
1.2.2 Dao接口
public interface UserDao {
//根據id查詢用戶
User queryUserById(int id);
//根據用戶名模糊查詢用戶
List<User> queryUserByUsername(String username);
//保存用戶
void saveUser(User user);
}
1.2.3 Dao的實現類
public class UserDaoImpl implements UserDao {
private SqlSessionFactory sqlSessionFactory;
public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
super();
this.sqlSessionFactory = sqlSessionFactory;
}
@Override
public User queryUserById(int id) {
// 創建SqlSession
SqlSession sqlSession = this.sqlSessionFactory.openSession();
// 執行查詢邏輯
User user = sqlSession.selectOne("queryUserById", id);
// 釋放資源
sqlSession.close();
return user;
}
@Override
public List<User> queryUserByUsername(String username) {
// 創建SqlSession
SqlSession sqlSession = this.sqlSessionFactory.openSession();
// 執行查詢邏輯
List<User> list = sqlSession.selectList("queryUserByUsername", username);
// 釋放資源
sqlSession.close();
return list;
}
@Override
public void saveUser(User user) {
// 創建SqlSession
SqlSession sqlSession = this.sqlSessionFactory.openSession();
// 執行保存邏輯
sqlSession.insert("saveUser", user);
// 提交事務
sqlSession.commit();
// 釋放資源
sqlSession.close();
}
}
1.2.4 Dao測試
創建一個JUnit的測試類,對UserDao進行測試,測試代碼如下:
public class UserDaoTest {
private SqlSessionFactory sqlSessionFactory;
@Before
public void init() throws Exception {
// 創建SqlSessionFactoryBuilder
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// 加載SqlMapConfig.xml配置文件
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
// 創建SqlsessionFactory
this.sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
}
@Test
public void testQueryUserById() {
// 創建DAO
UserDao userDao = new UserDaoImpl(this.sqlSessionFactory);
// 執行查詢
User user = userDao.queryUserById(1);
System.out.println(user);
}
@Test
public void testQueryUserByUsername() {
// 創建DAO
UserDao userDao = new UserDaoImpl(this.sqlSessionFactory);
// 執行查詢
List<User> list = userDao.queryUserByUsername("張");
for (User user : list) {
System.out.println(user);
}
}
@Test
public void testSaveUser() {
// 創建DAO
UserDao userDao = new UserDaoImpl(this.sqlSessionFactory);
// 創建保存對象
User user = new User();
user.setUsername("劉備");
user.setBirthday(new Date());
user.setSex("1");
user.setAddress("蜀國");
// 執行保存
userDao.saveUser(user);
System.out.println(user);
}
}
1.2.5 問題
原始Dao開發中存在以下問題:
Dao方法體存在重複代碼:通過SqlSessionFactory創建SqlSession,調用SqlSession的數據庫操作方法
調用sqlSession的數據庫操作方法需要指定statement的id,這裏存在硬編碼,不得於開發維護
1.3 Mapper動態代理方式(重點)
1.3.1 開發規範
Mapper接口開發方法只需要程序員編寫Mapper接口(相當於Dao接口),由Mybatis框架根據接口定義創建接口的動態代理對象,代理對象的方法體同上邊Dao接口實現類方法。
Mapper接口開發需要遵循以下規範:
1、 Mapper.xml文件中的namespace與mapper接口的類路徑相同。
2、 Mapper接口方法名和Mapper.xml中定義的每個statement的id相同
3、 Mapper接口方法的輸入參數類型和mapper.xml中定義的每個sql 的parameterType的類型相同
4、 Mapper接口方法的輸出參數類型和mapper.xml中定義的每個sql的resultType的類型相同
1.3.2 Mapper.xml(映射文件)
定義mapper映射文件UserMapper.xml,將UserMapper.xml放在config下mapper目錄下。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- namespace:命名空間,用於隔離sql -->
<!-- 還有一個很重要的作用,使用動態代理開發DAO,1. namespace必須和Mapper接口類路徑一致 -->
<mapper namespace="com.mybatis.mapper.UserMapper">
<!-- 根據用戶id查詢用戶 -->
<!-- 2. id必須和Mapper接口方法名一致 -->
<!-- 3. parameterType必須和接口方法參數類型一致 -->
<!-- 4. resultType必須和接口方法返回值類型一致 -->
<select id="queryUserById" parameterType="int"
resultType="com.mybatis.pojo.User">
select * from user where id = #{id}
</select>
<!-- 根據用戶名查詢用戶 -->
<select id="queryUserByUsername" parameterType="string"
resultType="com.mybatis.pojo.User">
select * from user where username like '%${value}%'
</select>
<!-- 保存用戶 -->
<insert id="saveUser" parameterType="com.mybatis.pojo.User">
<selectKey keyProperty="id" keyColumn="id" order="AFTER"
resultType="int">
select last_insert_id()
</selectKey>
insert into user(username,birthday,sex,address) values
(#{username},#{birthday},#{sex},#{address});
</insert>
</mapper>
1.3.3 UserMapper(接口文件)
public interface UserMapper {
//根據id查詢
User queryUserById(int id);
//根據用戶名查詢用戶
List<User> queryUserByUsername(String username);
//保存用戶
void saveUser(User user);
}
1.3.4 加載UserMapper.xml文件
修改SqlMapConfig.xml文件,添加以下所示的內容:
1.3.5 測試
public class UserMapperTest {
private SqlSessionFactory sqlSessionFactory;
@Before
public void init() throws Exception {
// 創建SqlSessionFactoryBuilder
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// 加載SqlMapConfig.xml配置文件
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
// 創建SqlsessionFactory
this.sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
}
@Test
public void testQueryUserById() {
// 獲取sqlSession,和spring整合後由spring管理
SqlSession sqlSession = this.sqlSessionFactory.openSession();
// 從sqlSession中獲取Mapper接口的代理對象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 執行查詢方法
User user = userMapper.queryUserById(1);
System.out.println(user);
// 和spring整合後由spring管理
sqlSession.close();
}
@Test
public void testQueryUserByUsername() {
// 獲取sqlSession,和spring整合後由spring管理
SqlSession sqlSession = this.sqlSessionFactory.openSession();
// 從sqlSession中獲取Mapper接口的代理對象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 執行查詢方法
List<User> list = userMapper.queryUserByUsername("張");
for (User user : list) {
System.out.println(user);
}
// 和spring整合後由spring管理
sqlSession.close();
}
@Test
public void testSaveUser() {
// 獲取sqlSession,和spring整合後由spring管理
SqlSession sqlSession = this.sqlSessionFactory.openSession();
// 從sqlSession中獲取Mapper接口的代理對象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 創建保存對象
User user = new User();
user.setUsername("劉備");
user.setBirthday(new Date());
user.setSex("1");
user.setAddress("蜀國");
// 執行查詢方法
userMapper.saveUser(user);
System.out.println(user);
// 和spring整合後由spring管理
sqlSession.commit();
sqlSession.close();
}
}
1.3.6 小結
selectOne和selectList
動態代理對象調用sqlSession.selectOne()和sqlSession.selectList()是根據mapper接口方法的返回值決定,如果返回list則調用selectList方法,如果返回單個對象則調用selectOne方法。
namespace
mybatis官方推薦使用mapper代理方法開發mapper接口,程序員不用編寫mapper接口實現類,使用mapper代理方法時,輸入參數可以使用pojo包裝對象或map對象,保證dao的通用性。
二、SqlMapConfig.xml配置文件
2.1 配置內容
SqlMapConfig.xml中配置的內容和順序如下:
properties(屬性)
settings(全局配置參數)
typeAliases(類型別名)
typeHandlers(類型處理器)
objectFactory(對象工廠)
plugins(插件)
environments(環境集合屬性對象)
environment(環境子屬性對象)
transactionManager(事務管理)
dataSource(數據源)
mappers(映射器)
2.2 properties(屬性)
SqlMapConfig.xml可以引用java屬性文件中的配置信息如下:
SqlMapConfig.xml引用如下:
注意: MyBatis 將按照下面的順序來加載屬性:
在 properties 元素體內定義的屬性首先被讀取。
然後會讀取properties 元素中resource或 url 加載的屬性,它會覆蓋已讀取的同名屬性。
2.3 typeAliases(類型別名)
2.4 mappers(映射器)
Mapper配置的幾種方法:
2.4.1 <mapper resource=" " />
使用相對於類路徑的資源(現在的使用方式)
如:<mapper resource=“sqlmap/User.xml” />
2.4.2 <mapper class=" " />
使用mapper接口類路徑
如:<mapper class=“com.mybatis.mapper.UserMapper”/>
注意:此種方法要求mapper接口名稱和mapper映射文件名稱相同,且放在同一個目錄中。
2.4.3 <package name=""/>
註冊指定包下的所有mapper接口
如:<package name=“com.mybatis.mapper”/>
注意:此種方法要求mapper接口名稱和mapper映射文件名稱相同,且放在同一個目錄中。
三、輸入映射和輸出映射
Mapper.xml映射文件中定義了操作數據庫的sql,每個sql是一個statement,映射文件是mybatis的核心。
3.1 parameterType(輸入類型)
3.1.1 傳遞簡單類型
使用#{}佔位符,或者${}進行sql拼接。
3.1.2 傳遞pojo對象
Mybatis使用ognl表達式解析對象字段的值,#{}或者${}括號中的值爲pojo屬性名稱。
3.1.3 傳遞pojo包裝對象
開發中通過可以使用pojo傳遞查詢條件。
查詢條件可能是綜合的查詢條件,不僅包括用戶查詢條件還包括其它的查詢條件(比如查詢用戶信息的時候,將用戶購買商品信息也作爲查詢條件),這時可以使用包裝對象傳遞輸入參數。
包裝對象:Pojo類中的一個屬性是另外一個pojo。
3.1.3.1 編寫QueryVo
3.1.3.2 Mapper.xml文件
在UserMapper.xml中配置sql,如下圖。
3.1.3.3 Mapper接口
在UserMapper接口中添加方法,如下圖
3.1.3.4 測試方法
在UserMapeprTest增加測試方法,如下:
@Test
public void testQueryUserByQueryVo() {
// mybatis和spring整合,整合之後,交給spring管理
SqlSession sqlSession = this.sqlSessionFactory.openSession();
// 創建Mapper接口的動態代理對象,整合之後,交給spring管理
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 使用userMapper執行查詢,使用包裝對象
QueryVo queryVo = new QueryVo();
// 設置user條件
User user = new User();
user.setUsername("張");
// 設置到包裝對象中
queryVo.setUser(user);
// 執行查詢
List<User> list = userMapper.queryUserByQueryVo(queryVo);
for (User u : list) {
System.out.println(u);
}
// mybatis和spring整合,整合之後,交給spring管理
sqlSession.close();
}
3.1.3.5 效果
3.2 resultType(輸出類型)
3.2.1 輸出簡單類型
3.2.1.1 Mapper.xml文件
3.2.1.2 Mapper接口
3.2.1.3 測試
@Test
public void testQueryUserCount() {
// mybatis和spring整合,整合之後,交給spring管理
SqlSession sqlSession = this.sqlSessionFactory.openSession();
// 創建Mapper接口的動態代理對象,整合之後,交給spring管理
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 使用userMapper執行查詢用戶數據條數
int count = userMapper.queryUserCount();
System.out.println(count);
// mybatis和spring整合,整合之後,交給spring管理
sqlSession.close();
}
3.2.2 輸出pojo對象
3.2.3 輸出pojo列表
3.3 resultMap
resultType可以指定將查詢結果映射爲pojo,但需要pojo的屬性名和sql查詢的列名一致方可映射成功。
如果sql查詢字段名和pojo的屬性名不一致,可以通過resultMap將字段名和屬性名作一個對應關係 ,resultMap實質上還需要將查詢結果映射到pojo對象中。
resultMap可以實現將查詢結果映射爲複雜類型的pojo,比如在查詢結果映射對象中包括pojo和list實現一對一查詢和一對多查詢。
3.3.1 聲明pojo對象
public class Order {
// 訂單id
private int id;
// 用戶id
private Integer userId;
// 訂單號
private String number;
// 訂單創建時間
private Date createtime;
// 備註
private String note;
get/set方法......
}
3.3.2 Mapper.xml文件
3.3.3 Mapper接口
3.3.4 測試
public class OrderMapperTest {
private SqlSessionFactory sqlSessionFactory;
@Before
public void init() throws Exception {
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
this.sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void testQueryAll() {
// 獲取sqlSession
SqlSession sqlSession = this.sqlSessionFactory.openSession();
// 獲取OrderMapper
OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class);
// 執行查詢
List<Order> list = orderMapper.queryOrderAll();
for (Order order : list) {
System.out.println(order);
}
}
}
3.3.5 效果
3.3.6 使用resultMap
由於上邊的mapper.xml中sql查詢列(user_id)和Order類屬性(userId)不一致,所以查詢結果不能映射到pojo中。
需要定義resultMap,把orderResultMap將sql查詢列(user_id)和Order類屬性(userId)對應起來
3.3.7 使用resultMap結果展示
四、動態sql
4.1 If標籤
4.2 Where標籤
4.3 Sql片段
Sql中可將重複的sql提取出來,使用時用include引用即可,最終達到sql重用的目的。
如果要使用別的Mapper.xml配置的sql片段,可以在refid前面加上對應的Mapper.xml的namespace。
4.4 foreach標籤
向sql傳遞數組或List,mybatis使用foreach解析,如下:根據多個id查詢用戶信息。
五、關聯查詢
5.1 商品訂單數據模型
5.2 一對一查詢
需求:查詢所有訂單信息,關聯查詢下單用戶信息。
注意:因爲一個訂單信息只會是一個人下的訂單,所以從查詢訂單信息出發關聯查詢用戶信息爲一對一查詢。如果從用戶信息出發查詢用戶下的訂單信息則爲一對多查詢,因爲一個用戶可以下多個訂單。
sql語句:
SELECT
o.id,
o.user_id userId,
o.number,
o.createtime,
o.note,
u.username,
u.address
FROM
`order` o
LEFT JOIN `user` u ON o.user_id = u.id
5.2.1 使用resultType
使用resultType,改造訂單pojo類,此pojo類中包括了訂單信息和用戶信息,這樣返回對象的時候,mybatis自動把用戶信息也注入進來了
5.2.1.1 改造pojo類
5.2.1.2 Mapper.xml
5.2.1.3 Mapper接口
5.2.1.4 測試
5.2.1.5 總結
定義專門的pojo類作爲輸出類型,其中定義了sql查詢結果集所有的字段。此方法較爲簡單,企業中使用普遍
5.2.2 使用resultMap
使用resultMap,定義專門的resultMap用於映射一對一查詢結果
5.2.2.1 改造pojo類
在Order類中加入User屬性,user屬性中用於存儲關聯查詢的用戶信息,因爲訂單關聯查詢用戶是一對一關係,所以這裏使用單個User對象存儲關聯查詢的用戶信息
5.2.2.2 Mapper.xml
<resultMap type="order" id="orderUserResultMap">
<id property="id" column="id" />
<result property="userId" column="user_id" />
<result property="number" column="number" />
<result property="createtime" column="createtime" />
<result property="note" column="note" />
<!-- association :配置一對一屬性 -->
<!-- property:order裏面的User屬性名 -->
<!-- javaType:屬性類型 -->
<association property="user" javaType="user">
<!-- id:聲明主鍵,表示user_id是關聯查詢對象的唯一標識-->
<id property="id" column="user_id" />
<result property="username" column="username" />
<result property="address" column="address" />
</association>
</resultMap>
<!-- 一對一關聯,查詢訂單,訂單內部包含用戶屬性 -->
<select id="queryOrderUserResultMap" resultMap="orderUserResultMap">
SELECT
o.id,
o.user_id,
o.number,
o.createtime,
o.note,
u.username,
u.address
FROM
`order` o
LEFT JOIN `user` u ON o.user_id = u.id
</select>
5.2.2.3 Mapper接口
5.2.2.4 測試
5.3 一對多查詢
案例:查詢所有用戶信息及用戶關聯的訂單信息。用戶信息和訂單信息爲一對多關係。
sql語句:
SELECT
u.id,
u.username,
u.birthday,
u.sex,
u.address,
o.id oid,
o.number,
o.createtime,
o.note
FROM
`user` u
LEFT JOIN `order` o ON u.id = o.user_id
5.3.1 改造pojo類
5.3.2 Mapper.xml
<resultMap type="user" id="userOrderResultMap">
<id property="id" column="id" />
<result property="username" column="username" />
<result property="birthday" column="birthday" />
<result property="sex" column="sex" />
<result property="address" column="address" />
<!-- 配置一對多的關係 -->
<collection property="orders" javaType="list" ofType="order">
<!-- 配置主鍵,是關聯Order的唯一標識 -->
<id property="id" column="oid" />
<result property="number" column="number" />
<result property="createtime" column="createtime" />
<result property="note" column="note" />
</collection>
</resultMap>
<!-- 一對多關聯,查詢訂單同時查詢該用戶下的訂單 -->
<select id="queryUserOrder" resultMap="userOrderResultMap">
SELECT
u.id,
u.username,
u.birthday,
u.sex,
u.address,
o.id oid,
o.number,
o.createtime,
o.note
FROM
`user` u
LEFT JOIN `order` o ON u.id = o.user_id
</select>