一个小案例
如果我不用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实现)