一個小案例
如果我不用id作爲主鍵,而是用UUID作爲主鍵,怎麼操作?
第一種方法
<insert id="insertUser" parameterType="com.bamzhy.bean.User">
INSERT INTO tt_user VALUES (#{id},#{username},#{password},#{email},#{age});
</insert>
@Test
public void test3() throws IOException {
//使用Mybatis的api去實現
//加載配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//操作數據庫的一個載體
SqlSession sqlSession = sqlSessionFactory.openSession();
//返回一個對象
User user = new User();
//Universally Unique Identifier(UUID 通用唯一識別碼)
user.setId(UUID.randomUUID().toString());
user.setUsername("haha4");
user.setPassword("12345");
user.setEmail("[email protected]");
user.setAge(199191);
int insert = sqlSession.insert("test.insertUser", user);
sqlSession.commit();
System.out.println("insert "+insert);
}
第二種方法
- 使用selectKey
<insert id="insertUser" parameterType="com.bamzhy.bean.User">
<!--keyProperty對應的是下邊{}中的名稱,resultType是其格式,order是執行時間在此條sql語句前還是語句後-->
<selectKey keyProperty="id" resultType="string" order="BEFORE">
SELECT UUID()
</selectKey>
INSERT INTO tt_user VALUES (#{id},#{username},#{password},#{email},#{age});
</insert>
使用MyBatis ORM實現DAO層
案例2實現之前代碼的重構
src下邊的mybatis.xml和user.xml跟之前的沒有區別(那些不用動)
- 新增UserDaoImpl.java
@Override
public User findUserById(int id) throws IOException {
//使用Mybatis的api去實現
//加載配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
//操作數據庫的一個載體
SqlSession sqlSession = sqlSessionFactory.openSession();
//返回一個對象
User user = sqlSession.selectOne("test.findUserById", id);
System.out.println("user"+user);
return null;
}
- 新增一個測試用例
@Test
public void findUserById() throws IOException {
UserDao user=new UserDaoImpl();
user.findUserById(2);
}
- 考慮到沒必要每一次操作都new一個sqlSessionFactory,所以我們把new這個操作的過程放進DAO中
對之前案例的反思
- 有大量代碼是重複的
- SqlSessionFactory,應該是全局範圍的,整個應用只有一個工廠。使用單例模式來實現。與spring集成了之後,由spring對其來進行單例管理。
- SqlSession。它內部含有一塊數據區域,操作數據庫的時候是需要通過它去設置隔離級別等事務特性。如果放在成員變量中使用spring對其來進行單例管理,則在多線程的情況下會存在線程安全問題。所以應該將sqlSession聲明在方法內部。 這樣每個線程用自己的sqlSession,不共享數據,則不存在線程安全問題。
從全新角度出發解決問題(Mapper動態代理實現)
開發規範
Mapper接口的全限定名要和Mapper映射文件的namespace一致
Mapper接口的方法名稱要和Mapper映射文件的statement id一致
Mapper接口的方法參數類型要和Mapper映射文件的statement parameter Type類型一致,而且它的參數是一個(多個參數也沒關係,可以使用map裝進去)
- Mapper接口的方法返回值類型要和Mapper映射文件的statement的Result Type一致
如果是List<>的話,需要保證其泛型類型與resultType保持一致(每一行的類型)
- bean裏邊的xml文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bamzhy.dao.UserDao">
<!--parameterType是參數的類型,以前的問號?用#{}代替-->
<!--這一行SQL語句要映射到哪個類上要使用resultType指明-->
<select id="findUserById" parameterType="int" resultType="com.bamzhy.bean.User">
SELECT * FROM tt_user WHERE id=#{id};
</select>
<select id="findUserByName" parameterType="string" resultType="com.bamzhy.bean.User">
SELECT * FROM tt_user WHERE username Like #{id};
</select>
<insert id="insertUser" parameterType="com.bamzhy.bean.User">
<!--keyProperty對應的是下邊{}中的名稱,resultType是其格式,order是執行時間在此條sql語句前還是語句後-->
<!--<selectKey keyProperty="id" resultType="string" order="BEFORE">-->
<!--SELECT UUID()-->
<!--</selectKey>-->
<selectKey keyProperty="id" resultType="string" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
INSERT INTO tt_user VALUES (#{id},#{username},#{password},#{email},#{age});
</insert>
<delete id="deleteUserById" parameterType="int">
DELETE FROM tt_user WHERE id=#{id};
</delete >
<update id="updateUserById" parameterType="com.bamzhy.bean.User">
UPDATE tt_user SET username=#{username},password=#{password} WHERE id=#{id};
</update>
</mapper>
- Dao接口
package com.bamzhy.dao;
import com.bamzhy.bean.User;
import java.io.IOException;
import java.util.List;
public interface UserDao {
User findUserById(int id) throws IOException;
User finduUserByName(String username) throws IOException;
int AddUser(User user) throws IOException;
List<User> queryAllUser( ) throws IOException;
}
- test
package com.bamzhy.test;
import com.bamzhy.bean.User;
import com.bamzhy.dao.UserDao;
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 org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
public class test {
SqlSessionFactory sqlSessionFactory ;
UserDao dao;
SqlSession sqlSession;
//在任何一個測試方法之前執行
@Before
public void before() throws Exception {
//加載配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
sqlSession = sqlSessionFactory.openSession();
dao = sqlSession.getMapper(UserDao.class);
}
//在任何一個測試方法之後執行
@After
public void After(){
sqlSession.commit();
sqlSession.close();
}
@Test
public void test6() throws IOException {
User userById = dao.findUserById(1);
System.out.println("user=" + userById);
}
}
使用Mapper動態代理的好處
- 使用之前的session,selectOne(“test.findUserById”,1),通過字符串調用標籤定義的SQL,一是容易出錯,而是XML中的id修改以後那麼用了這個id的程序就都得改,這樣比較麻煩
- 爲了防止這些現象的出現,採用了Mapper方法
使用Mapper動態代理的流程
- 我們定義一個接口類,裏邊函數名和需要與Bean中的XML文件定義的id保持一致。
- 把Bean中的namespace替換成接口類的全類名
- 獲取session,並獲取Mapper接口的代理對象(這個可用使用AOP放在方法執行前執行)
//加載配置文件
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
sqlSession = sqlSessionFactory.openSession();
//獲取代理對象
dao = sqlSession.getMapper(UserDao.class);
- 調用代理對象方法,關閉session(也用AOP實現)