使用Mybatis開發Dao,通常有兩個方法,即原始Dao開發方法和Mapper接口開發方法,常用還是Mapper接口開發。
public class test1 {
private static SqlSessionFactory sqlSessionFactory;
private static Reader reader;
//創建會話工廠,傳入mybatis的配置文件信息
static{
try{
//得到配置文件流
reader = Resources.getResourceAsReader("Configuration.xml");
//創建會話工廠,傳入mybatis的配置文件信息
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
}catch(Exception e){
e.printStackTrace();
}
}
//查詢數據
public void select() {
//通過工廠得到sqlsession
SqlSession session = sqlSessionFactory.openSession();
try {
//通過SqlSession操作數據庫//第一個參數:映射文件中statement的id//第二個參數:指定和映射文件所匹配的parameterType類型參數
User user = (User) session.selectOne("com.yihaomen.mybatis.model.User.selectUserByID", 1);
} finally {
session.close();
}
}
}
想要通過mabatis對數據庫操作,除了要各種配置之外,最終在測試的時候,需要創建特定對象,上圖示例:
1、SqlSessionFactoryBuilder
SqlSessionFactoryBuilder用於創建SqlSessionFacoty,SqlSessionFacoty一旦創建完成就不需要SqlSessionFactoryBuilder了,因為SqlSession是通過SqlSessionFactory生產,所以可以將SqlSessionFactoryBuilder當成一個工具類使用,最佳使用范圍是方法范圍即方法體內局部變量。
2、SqlSessionFactory
SqlSessionFactory是一個接口,接口中定義了openSession的不同重載方法,SqlSessionFactory的最佳使用范圍是整個應用運行期間,一旦創建後可以重復使用,通常以單例模式管理SqlSessionFactory
3、SqlSession
SqlSession是一個面向用戶的接口, sqlSession中定義了數據庫操作,默認使用DefaultSqlSession實現類。
SqlSession中封裝了對數據庫的操作,如:查詢、插入、更新、刪除等。通過SqlSessionFactory創建SqlSession,而SqlSessionFactory是通過SqlSessionFactoryBuilder進行創建。
每個線程都應該有它自己的SqlSession實例。SqlSession的實例不能共享使用,它也是線程不安全的。因此最佳的范圍是請求或方法范圍。絕對不能將SqlSession實例的引用放在一個類的靜態字段或實例字段中。
打開一個 SqlSession;使用完畢就要關閉它。通常把這個關閉操作放到 finally 塊中以確保每次都能執行關閉。
//通過工廠得到sqlsession
SqlSession session = sqlSessionFactory.openSession();
try {
} finally {
session.close();
}
程序員需要編寫DAO和DAO的實現類。需要向DAO實現類中注入SqlSessionFactory,在方法體內通過SqlSessionFactory來創建SqlSession。
1、定義Dao接口
public interface UserDAO{
//根據本id查詢用戶
User findUserById(int id) throws Exception;
//添加用戶
void insertUser(User user) throws Exception;
//根據id刪除用戶
void deleteUser(int id) throws Exception;
}
2、Dao實現類
DAO實現類
/**
* @ClassName: UserDAOImpl
* @Description: DAO實現類(注意:SqlSession是非線程安全的,故不能聲明為全局的)
*/
public class UserDAOImpl implements UserDAO {
SqlSessionFactory sqlSessionFactory;
/**
* 向DAO實現類中注入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();
}
}
3、新建一個源代碼目錄命名為test,在UserDAOImpl類中鼠標右鍵新建一個Junit Test Case,更換源代碼目錄為test並勾選我們需要測試的方法:
public class UserDAOImplTest {
private SqlSessionFactory sqlSessionFactory;
/**
* 此方法在執行測試之前執行,得到一個SqlSessionFactory
*/
@Before
public void setUp() throws Exception {
sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("SqlMapConfig.xml"));
}
@Test
public void testFindUserById() throws Exception {
// 創建UserDAO(在構造中注入SqlSessionFactory)
UserDAO userDAO = new UserDAOImpl(sqlSessionFactory);
User user = userDAO.findUserById(1);
System.out.println(user);
}
@Test
public void testInsertUser() throws Exception {
UserDAO userDAO = new UserDAOImpl(sqlSessionFactory);
User user = new User();
user.setSex("2");
user.setUsername("孫悟空");
user.setAddress("方寸靈台山");
user.setBirthday(new Date());
userDAO.insertUser(user);
}
@Test
public void testDeleteUser() throws Exception {
UserDAO userDAO = new UserDAOImpl(sqlSessionFactory);
userDAO.deleteUser(27);
}
}
DAO的接口實現類中存在大量的模板方法,設想:可以將重復的代碼提取出來。
在SqlSession的方法時將Statement的id硬編碼在你DAO的實現類中。
調用sqlSession的相關方法傳入參數是泛型,即使傳入錯誤的參數,在編譯階段也不會報錯,不利於程序員開發。
Mapper接口開發方法只需要程序員編寫Mapper接口(相當於Dao接口),由Mybatis框架根據接口定義創建接口的動態代理對象,代理對象的方法體同上邊Dao接口實現類方法。
Mapper接口開發需要遵循以下規范:Mapper可以自動生成Mapper接口實現類代理對象。
1、 Mapper.xml文件中的namespace與mapper接口的類路徑相同。
2、 Mapper接口方法名和Mapper.xml中定義的每個statement的id相同
3、 Mapper接口方法的輸入參數類型和mapper.xml中定義的每個sql 的parameterType的類型相同
4、 Mapper接口方法的輸出參數類型和mapper.xml中定義的每個sql的resultType的類型相同
<?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進行分類化管理,注意:使用mapper代理方法開發,namespace有特殊重要作用 -->
<mapper namespace="com.yihaomen.mybatis.dao.IUserOperation">
<!-- 在映射文件中配置很多sql -->
<!-- id標識映射文件的sql,稱為statement的id ,將sql語句封裝到mappedStatement對象中,所以將id稱為statement的id
parameterType:指定輸入類型 resultType:指定sql輸出結果的所映射的java對象,select指定resultType表示將單挑記錄
映射成java對象-->
<select id="selectUserByID" parameterType="int" resultType="User">
select * from `user` where id = #{id}
</select>
<insert id="addUser" parameterType="User"
useGeneratedKeys="true" keyProperty="id">
insert into user(userName,userAge,userAddress) values(#{userName},#{userAge},#{userAddress})
</insert>
<update id="updateUser" parameterType="User">
update user set userName=#{userName},userAge=#{userAge},userAddress=#{userAddress} where id=#{id}
</update>
<delete id="deleteUser" parameterType="int">
delete from user where id=#{id}
</delete>
<select id="list" resultType="User">
select * from `user`
</select>
<!-- ${}表示拼接sql串,指定就是單挑記錄所映射的java對象類型,使用${}拼接,容易導致sql注入
${value}:拼接輸入參數的內容,如果傳入類型是簡單類型,${}中只能使用value -->
<select id="findUserByName" parameterType="String" resultType="User">
select * from `user` where username like '%${value}%'
</select>
</mapper>
package com.yihaomen.mybatis.dao;
import java.util.List;
import com.yihaomen.mybatis.model.Article;
import com.yihaomen.mybatis.model.User;
//注意:接口名字必須與 xml中的namespace名字一樣 2、接口實現方法每個名字 與xml中的id對應
public interface IUserOperation {
//查詢數據
public User selectUserByID(int id);
//增加數據
public void addUser(User user);
//更新數據
public void updateUser(User user);
//刪除數據
public void deleteUser(int id);
//聯合查詢
public List<Article> getUserArticles(int id);
//list獲取
public List<User> list();
//模糊查詢
public List<User> findUserByName(String name);
}
完成前面2步之後不要忘了在映射文件SqlMapConfig.xml中加載UserMapper.xml哦!
<!-- 配置映射文件 -->
<mappers>
<mapper resource="sqlmap/User.xml"/>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
3、針對UserMapper接口測試
package com.yihaomen.mybatis.ui;
import java.io.Reader;
import java.util.List;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import com.yihaomen.mybatis.dao.IUserOperation;
import com.yihaomen.mybatis.model.User;
public class Test {
private static SqlSessionFactory sqlSessionFactory;
private static Reader reader;
//創建會話工廠,傳入mybatis的配置文件信息
static{
try{
//得到配置文件流
reader = Resources.getResourceAsReader("Configuration.xml");
//創建會話工廠,傳入mybatis的配置文件信息
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
}catch(Exception e){
e.printStackTrace();
}
}
//公共方法,返回初始化的sqlSessionFactory對象
public static SqlSessionFactory getSession(){
return sqlSessionFactory;
}
//查詢數據
public void select() {
//通過工廠得到sqlsession
SqlSession session = sqlSessionFactory.openSession();
try {
//通過SqlSession操作數據庫
//第一個參數:映射文件中statement的id
//第二個參數:指定和映射文件所匹配的parameterType類型參數
User user = (User) session.selectOne("com.yihaomen.mybatis.model.User.selectUserByID", 1);
System.out.println(user.getUserAddress());
System.out.println(user.getUserName());
} finally {
session.close();
}
}
//增加數據
public void addUser(String address,String name){
//創建user對象
User user=new User();
user.setUserAddress(address);
user.setUserName(name);
user.setUserAge(80);
//通過工廠得到SqlSession
SqlSession session = sqlSessionFactory.openSession();
try {
IUserOperation userOperation=session.getMapper(IUserOperation.class);
//添加數據
userOperation.addUser(user);
//提交
session.commit();
//System.out.println("當前增加的用戶 id為:"+user.getId());
} finally {
session.close();
}
}
//更新數據
public void updateUser(int id,String address){
//先得到用戶,然後修改,提交。
SqlSession session = sqlSessionFactory.openSession();
try {
IUserOperation userOperation = session.getMapper(IUserOperation.class);
User user = userOperation.selectUserByID(id);
user.setUserAddress(address);
userOperation.updateUser(user);
session.commit();
System.out.println("更新成功!!");
} finally {
session.close();
}
}
//刪除數據
public void deleteUser(int id) {
SqlSession session = sqlSessionFactory.openSession();
try{
IUserOperation userOperation = session.getMapper(IUserOperation.class);
userOperation.deleteUser(id);
session.commit();
System.out.println("刪除數據:id= "+id);
}finally {
session.close();
}
}
//list獲取
public void getList() {
SqlSession session = sqlSessionFactory.openSession();
try{
IUserOperation userOperation = session.getMapper(IUserOperation.class);
List<User> us = userOperation.list();
session.commit();
//System.out.println("生成list: "+us.size());
}finally {
session.close();
}
}
//模糊查詢
public void geFindUserByName(String name) {
SqlSession session = sqlSessionFactory.openSession();
try{
IUserOperation userOperation = session.getMapper(IUserOperation.class);
List<User> us = userOperation.findUserByName(name);
System.out.println(us.size());
session.commit();
}finally {
session.close();
}
}
public static void main(String[] args) {
Test test = new Test();
//test.getList();
test.geFindUserByName("小");
//test.addUser("杭州","小江");
}
}
我們比較疑問的就是我們在UserMapper.xml的根據用戶名查找用戶的ResultType使用的是普通的POJO,但是我們自己的Mapper接口中返回值是List類型。這不就造成了類型不一致麼?但是,實際上代理對象內部調用了selectOne()或者selectList(),代理對象內部會自動進行判斷是否是單獨的POJO選用合適的方法。
Mapper接口中方法的參數只有一個是否會影響系統的維護?DAO層的代碼是被業務層公用的,即使Mapper接口的參數只有一個我們也可以使用包裝的POJO來滿足系統需求。
注意:持久層中方法的參數中可以使用包裝類型,但是Service層中不建議使用包裝類型(不利於業務層的拓展維護)。