mybatis——開發dao層
文章目錄
- 1 SqlSession生命週期
- 2 原始Dao開發方式
- 3 Mapper動態代理開發方式
- 3.1 Mapper介面開發需要遵循以下規範:
- 3.2 編寫Mapper.xml(對映檔案)
- 3.3 編寫Mapper介面
- 3.4 SqlMapConfig.xml
- 3.5 UserMapperTest
- 4 小結
1 SqlSession生命週期
- sqlsession:方法級別
- sqlsessionFactory:全域性範圍(應用級別)
- sqlsessionFactoryBuilder:方法級別
1.1 SqlSessionFactoryBuilder
SqlSessionFactoryBuilder用於建立SqlSessionFacoty,SqlSessionFacoty一旦建立完成就不需要SqlSessionFactoryBuilder了,因為SqlSession是通過SqlSessionFactory生產,所以可以將SqlSessionFactoryBuilder當成一個工具類使用,最佳使用範圍是方法範圍(即方法體內區域性變數)。
1.2 SqlSessionFactory
SqlSessionFactory是一個介面,介面中定義了openSession的不同過載方法,SqlSessionFactory的最佳使用範圍是整個應用執行期間,一旦建立後可以重複使用,通常以單例模式管理SqlSessionFactory。
1.3 SqlSession
SqlSession中封裝了對資料庫的操作,如查詢、插入、更新、刪除等。通過SqlSessionFactory建立SqlSession,而SqlSessionFactory是通過SqlSessionFactoryBuilder進行建立的。
SqlSession是一個面向程式設計師的介面,SqlSession中定義了資料庫操作方法,所以SqlSession作用是操作資料庫,並且SqlSession物件要儲存資料庫連線、事務和一級快取結構等。
每個執行緒都應該有它自己的SqlSession例項。SqlSession的例項不能共享使用,它是執行緒不安全的(多執行緒訪問系統,當多執行緒同時使用一個SqlSession物件時會造成資料衝突問題)。由於SqlSession物件是執行緒不安全的,因此它的最佳使用範圍是請求或方法範圍(也可說為SqlSession的最佳使用場合是在方法體內作為區域性變數來使用),絕對不能將SqlSession例項的引用放在一個類的靜態欄位或例項欄位中。
開啟一個SqlSession,使用完畢就要關閉它。通常把這個關閉操作放到finally塊中以確保每次都能執行關閉。如下:
SqlSession session = sqlSessionFactory.openSession();
try {
} finally {
session.close();
}
2 原始Dao開發方式
原始Dao開發方法需要程式設計師自己編寫Dao介面和Dao實現類。
2.1 UserMapper.xml和SqlMapConfig.xml
UserMapper.xml
<?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">
<mapper namespace="user">
<select id="findUserById" parameterType="int" resultType="User">
SELECT * from user WHERE id=#{id}
</select>
<insert id="addUser" parameterType="User">
<!-- selectKey將主鍵返回,需要再返回 -->
<selectKey keyProperty="id" order="AFTER" resultType="int">
select LAST_INSERT_ID()
</selectKey>
INSERT INTO user(username,password,email,phone,address)
VALUES (#{username},#{password},#{email},#{phone},#{address})
</insert>
<select id="getUserByName" parameterType="String" resultType="user">
SELECT * FROM user where username=#{username}
</select>
</mapper>
SqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="db.properties"></properties>
<typeAliases>
<typeAlias type="pojo.User" alias="user"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${db.driver}" />
<property name="url" value="${db.url}" />
<property name="username" value="${db.username}" />
<property name="password" value="${db.password}" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/UserMapper.xml" />
</mappers>
</configuration>
2.2 UserDao和UserDaoImp
public interface UserDao {
User findUserbyId(int id);
void addUser(User user);
List<User> getUserByName(String username);
}
UserDaoImpl.java:
public class UserDaoImp implements UserDao {
private SqlSessionFactory sqlSessionFactory;
public UserDaoImp(SqlSessionFactory sqlSessionFactory){
this.sqlSessionFactory =sqlSessionFactory;
}
public User findUserbyId(int id) {
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = sqlSession.selectOne("findUserById",id);
return user;
}
public void addUser(User user) {
SqlSession sqlSession = sqlSessionFactory.openSession();
sqlSession.insert("addUser", user);
sqlSession.commit();
if (sqlSession!=null)
sqlSession.close();
}
public List<User> getUserByName(String username) {
SqlSession sqlSession = sqlSessionFactory.openSession();
List<User> users =sqlSession.selectList("getUserByName",username);
return users;
}
}
2.2 測試
public class UserDaoTest {
private SqlSessionFactory sqlSessionFactory = null; // 工廠物件一般在我們的系統中是單例的
@Before
public void init() throws IOException {
// 第一步,建立SqlSessionFactoryBuilder物件
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// 第二步,載入配置檔案
InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml");
// 第三步,建立SqlSessionFactory物件
sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
}
@Test
public void testFindUserById(){
UserDao userDao = new UserDaoImp(sqlSessionFactory);
User u =userDao.findUserbyId(1);
System.out.println(u);
}
@Test
public void testAddUser(){
UserDao userDao = new UserDaoImp(sqlSessionFactory);
User user = new User("小xiao小", "111", "[email protected]", "123", "sasas");
userDao.addUser(user);
}
@Test
public void testGetUserByName(){
UserDao userDao = new UserDaoImp(sqlSessionFactory);
List<User> users =userDao.getUserByName("小小");
for (User u:users)
System.out.println(u);
}
}
2.3 原始Dao開發方式所帶來的問題
從以上UserDaoImpl類的程式碼可看出原始Dao開發存在以下問題:
- dao介面實現類方法中存在大量的重複程式碼,這些重複的程式碼就是模板程式碼。
模板程式碼為:
先建立sqlsession
再呼叫sqlsession的方法
再提交sqlsession
再關閉sqlsession
設想能否將這些程式碼提取出來,這可大大減輕程式設計師的工作量。 - 呼叫sqlSession的資料庫操作方法需要指定statement的id,這裡存在硬編碼,不得於開發維護。
- 呼叫sqlsession方法時傳入的變數,由於sqlsession方法使用泛型,即使變數型別傳入錯誤,在編譯階段也不報錯,不利於程式設計師開發。
3 Mapper動態代理開發方式
設計模式——動態代理
Mapper介面開發方法只需要程式設計師編寫Mapper介面,由Mybatis框架根據介面定義建立介面的動態代理物件,代理物件的方法體同上邊Dao介面實現類方法。
3.1 Mapper介面開發需要遵循以下規範:
Mapper.xml檔案中的namespace與mapper介面的類路徑相同,即namespace必須是介面的全限定名。
Mapper介面方法名和Mapper.xml中定義的每個statement的id相同。
Mapper介面方法的輸入引數型別和mapper.xml中定義的每個sql的parameterType的型別相同。
Mapper介面方法的輸出引數型別和mapper.xml中定義的每個sql的resultType的型別相同。
3.2 編寫Mapper.xml(對映檔案)
我們可在config原始碼目錄下新建一個mapper的普通資料夾,該資料夾專門用於存放對映檔案。然後在該資料夾下建立一個名為mapper.xml的對映檔案,內容如下:
<?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">
<mapper namespace="mapper.UserMapper">
<select id="findUserById" parameterType="int" resultType="User">
SELECT * from user WHERE id=#{id}
</select>
<insert id="addUser" parameterType="User">
<!-- selectKey將主鍵返回,需要再返回 -->
<selectKey keyProperty="id" order="AFTER" resultType="int">
select LAST_INSERT_ID()
</selectKey>
INSERT INTO user(username,password,email,phone,address)
VALUES (#{username},#{password},#{email},#{phone},#{address})
</insert>
<select id="getUserByName" parameterType="String" resultType="user">
SELECT * FROM user where username=#{username}
</select>
</mapper>
3.3 編寫Mapper介面
建立一個Mapper介面——UserMapper.java:
public interface UserMapper {
User findUserById(Integer id);
void addUser(User user);
List<User> getUserByName(String username);
}
3.4 SqlMapConfig.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="db.properties"></properties>
<typeAliases>
<!-- <typeAlias type="pojo.User" alias="user"/>-->
<package name="pojo"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${db.driver}" />
<property name="url" value="${db.url}" />
<property name="username" value="${db.username}" />
<property name="password" value="${db.password}" />
</dataSource>
</environment>
</environments>
<mappers>
<!--<mapper resource="mapper/UserMapper.xml" />-->
<package name="mapper"/>
</mappers>
</configuration>
3.5 UserMapperTest
public class UserMapperTest {
private SqlSessionFactory sqlSessionFactory;
@Before
public void creatSqlSessionFactory() throws IOException {
String resource = "SqlMapConfig.xml";
InputStream in = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
}
@Test
public void testFindUserById(){
// 和Spring整合後就省略了
SqlSession sqlSession = sqlSessionFactory.openSession();
// 獲得代理物件(到時候就只需要通過Spring容器拿到UserMapper介面的代理物件就可以了)
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User u =userMapper.findUserById(1);
System.out.println(u);
}
@Test
public void testAddUser(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
User user = new User("小xiao小", "111", "[email protected]", "123", "sasas");
userMapper.addUser(user);
}
@Test
public void testGetUserByName(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> users =userMapper.getUserByName("小小");
for (User u:users)
System.out.println(u);
}
}
4 小結
4.1 selectOne和selectList
動態代理物件呼叫sqlSession.selectOne()和sqlSession.selectList()是根據mapper介面方法的返回值決定,如果返回list則呼叫selectList方法,如果返回單個物件則呼叫selectOne方法。
4.2 namespace
mybatis官方推薦使用mapper代理方法開發mapper介面,程式設計師不用編寫mapper介面實現類,使用mapper代理方法時,輸入引數可以使用pojo包裝物件或map物件,保證dao的通用性。
4.3 SqlMapConfig.xml配置檔案
SqlMapConfig.xml檔案中配置的內容和順序如下:
properties(屬性)
settings(全域性配置引數)
typeAliases(類型別名)
typeHandlers(型別處理器)
objectFactory(物件工廠)
plugins(外掛)
environments(環境集合屬性物件)
environment(環境子屬性物件)
transactionManager(事務管理)
dataSource(資料來源)
mappers(對映器)
properties(屬性)
如果不按順序配置,xml會報錯
4.3.1 資料庫配置
資料配置屬性方法一
<properties>
<property name="jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="jdbc.url" value="jdbc:mysql://localhost:3306/shop?useUnicode=true&characterEncoding=utf8"/>
</properties>
資料配置屬性方法二
db.driver=com.mysql.jdbc.Driver
db.url = jdbc:mysql://localhost:3306/shop?useUnicode=true&characterEncoding=utf8
db.username = root
db.password=root
<properties resource="db.properties"></properties>
<dataSource type="POOLED">
<property name="driver" value="${db.driver}" />
<property name="url" value="${db.url}" />
<property name="username" value="${db.username}" />
<property name="password" value="${db.password}" />
</dataSource>
載入properties順序:
- 先讀取在properties元素體內定義的屬性
- 然後會讀取properties元素中resource或url載入的屬性,它會覆蓋已讀取的同名屬性
4.3.2 typeAliases(類型別名)
1. mybatis支援別名
別名 | 對映的型別 |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
map | Map |
2. 自定義別名
2.1配置單個別名
<typeAliases>
<!-- 單個別名定義 別名不區分大小寫 -->
<typeAlias type="pojo.User" alias="user"/>
</typeAliases>
2.2配置掃描包的別名
<!-- 配置pojo的別名 -->
<typeAliases>
<!-- 批量別名定義,掃描包的形式建立別名,別名就是類名,且不區分大小寫 -->
<package name="pojo"/>
</typeAliases>
4.3.3 SqlMapConfig.xml檔案載入mapper.xml檔案
<mappers>
<!-- 資源路徑載入 -->
<mapper resource="mapper/UserMapper.xml" />
<!-- 類路徑載入 -->
<!--注意:此種方法要求mapper介面名稱和mapper對映檔名稱相同,且放在同一個目錄中。-->
<mapper class="mapper/UserMapper.xml"/>
<!--注意:此種方法要求mapper介面名稱和mapper對映檔名稱相同,且放在同一個目錄中。-->
<package name="mapper"/>
</mappers>