使用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層中不建議使用包裝類型(不利於業務層的拓展維護)。