深入浅出Mybatis(五)mapper代理方法使用
一、前言
在前面分享了Mybatis的一些增删改查的操作过程,在接下来的过程中呢,可以说是涉及到一些框架的知识了,继续学习使用mapper代理方法。
从原始三层的架构到Mapper代理
下面这张图是最原始的三层架构的图,分成了UBD三个层,不同的层处理不同的事务。可以看出,当B和D之间的接触比较多的时候会对数据库造成很大的压力,数据库就会有多次的开闭。
针对以上的问题,我们做了如下的改变,就在BD两层之间添加了IDAL一个接口层,这样B层对D层的所有的交互都处理在IDAL层上,D层通过实现这个接口,就可以减少对D层的访问线程数目。达到了一个解耦的效果。但是还是不能避免我们的D层要写好固定的sql语句,即硬编码。D层的结构就会很固定,不灵活。
接下来就是我们通过使用Mybatis的框架把所有的sql语句写到Mapper.xml文件中,然后在全局配置文件中映射。Mapper.java通过一定的规范来提供方法。而且在SqlMapConfig.xml中配置数据库连接池,就减少了数据库的开闭,提高了数据库使用的效率。
实现原理
Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao接口),由Mybatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。
Mapper接口开发需要遵循以下规范:
1、 Mapper.xml文件中的namespace与mapper接口的类路径相同。
2、 Mapper接口方法名和Mapper.xml中定义的每个statement的id相同
3、 Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同
4、 Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同
操作
Mapper.xml映射文件
定义mapper映射文件UserMapper.xml(内容同Users.xml),需要修改namespace的值为 UserMapper接口路径。将UserMapper.xml放在classpath 下mapper目录 下。
<?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="com.dtt.mybatis.mapper.UserMapper"><!-- namespace:命名空间,用于隔离sql,还有一个很重要的作用,后面会讲 --> <!-- id:statement的id 或者叫做sql的id--> <!-- parameterType:声明输入参数的类型 --> <!-- resultType:声明输出结果的类型,应该填写pojo的全路径 --> <!-- #{}:输入参数的占位符,相当于jdbc的? --> <!-- 根据id获取用户信息 --> <select id="findUserById" parameterType="int" resultType="com.dtt.mybatis.pojo.User" > select * from user where id = #{id} </select> <!-- 根据用户名,查询用户列表,支持模糊查询 --> <select id="findUserByUsername" parameterType="java.lang.String" resultType="com.dtt.mybatis.pojo.User"> select * from user where username like '%${value}%' </select> <insert id="insertUser" parameterType="com.dtt.mybatis.pojo.User"> <!-- 将插入数据的主键返回,返回到user对象中。 select LAST_INSERT_ID():得到刚insert进行的记录的主键,只适用于自增主键 keyProperty:将查询到主键值设置到parameterType指定的对象的哪个属性。 order:select LAST_INSERT_ID()执行顺序,相对insert语句来说他的执行顺序 resultType:指定select LAST_INSERT_ID()的返回数据的类型 --> <selectKey keyProperty="id" order="AFTER" resultType="java.lang.Integer"> select LAST_INSERT_ID() </selectKey> insert into user (username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address}) </insert> <delete id="delUserById" parameterType="int"> delete from user where id=#{id} </delete> <update id="updateUserById" parameterType="com.dtt.mybatis.pojo.User"> update user set username=#{username} where id=#{id} </update> </mapper>
Mapper.java(接口文件)
代码如下:
package com.dtt.mybatis.mapper; import com.dtt.mybatis.pojo.User; import java.util.List; /** * 用户管理mapper */public interface UserMapper { //根据用户id查询用户信息 public User findUserById(int id) throws Exception; //查询用户列表 public List<User> findUserByUsername(String username) throws Exception; //添加用户信息 public void insertUser(User user)throws Exception; }
接口定义有如下特点:
1、 Mapper接口方法名和Mapper.xml中定义的statement的id相同
2、 Mapper接口方法的输入参数类型和mapper.xml中定义的statement的parameterType的类型相同
3、 Mapper接口方法的输出参数类型和mapper.xml中定义的statement的resultType的类型相同
SqlMapConfig.xml加载映射文件
修改SqlMapConfig.xml文件:
<!-- 加载映射文件 --> <mappers> <mapper resource="mapper/UserMapper.xml"/> </mappers>
测试代码如下:
package com.dtt.mybatis.test; import com.dtt.mybatis.pojo.User; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Before; import org.junit.Test; import java.io.IOException; import java.io.InputStream; import java.util.Date; import java.util.List; public class UserTest { //会话工厂 private SqlSessionFactory sqlSessionFactory; // Before在开始测试之前加载的方法,主要是创建会话工厂 @Before public void createSqlSessionFactory() throws IOException { //配置文件 String resource = "SqlMapConfig.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); // 使用SqlSessionFactoryBuilder从xml配置文件中创建SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } @Test public void testFindUserById() { //数据会话实例 SqlSession sqlSession = null; try { /* 创建数据库会话实例 */ sqlSession = sqlSessionFactory.openSession(); // 查询单个记录,根据用户id查询用户信息 User user = sqlSession.selectOne("test.findUserById", 10); // 输出用户信息 System.out.println(user); } catch (Exception e) { e.printStackTrace(); } finally { if (sqlSession != null) { sqlSession.close(); } } } // 根据用户名称模糊查询用户信息 @Test public void testFindUserByUsername() { // 数据库会话实例 SqlSession sqlSession = null; try { // 创建数据库会话实例 sqlSession = sqlSessionFactory.openSession(); // 查询单个记录,根据用户id查询用户信息 List<User> list = sqlSession.selectList("test.findUserByUsername", "小"); System.out.println(list.size()); } catch (Exception e) { e.printStackTrace(); } finally { if (sqlSession != null) { sqlSession.close(); } } } @Test public void testInsertUser(){ //数据库会话 SqlSession sqlSession=null; try { sqlSession= sqlSessionFactory.openSession(); //添加数据信息 User user = new User(); user.setSex("2"); user.setAddress("sadfsafsafs"); user.setBirthday(new Date()); user.setUsername("婷婷"); sqlSession.insert("test.insertUser", user); sqlSession.commit(); System.out.println("添加成功)"); System.out.println(user.getId()); }catch( Exception e){ e.printStackTrace(); } finally{ if(sqlSession!=null){ sqlSession.close(); } } } // 根据id删除用户 @Test public void testDelete() { // 数据库会话实例 SqlSession sqlSession = null; try { // 创建数据库会话实例 sqlSession = sqlSessionFactory.openSession(); // 删除用户 sqlSession.delete("test.delUserById",55); // 提交事务 sqlSession.commit(); System.out.println("删除成功"); } catch (Exception e) { e.printStackTrace(); } finally { if (sqlSession != null) { sqlSession.close(); } } } @Test public void testUpateUser(){ // 数据库会话实例 SqlSession sqlSession = null; try { // 创建数据库会话实例 sqlSession = sqlSessionFactory.openSession(); // 添加用户 User user =new User (); user.setId(20); user.setSex("1"); user.setAddress("浙江"); user.setBirthday(new Date()); user.setUsername("凤凰女"); sqlSession.update("test.updateUserById",user); sqlSession.commit(); System.out.println(user.getId()); System.out.println(user.getUsername()); } catch (Exception e) { e.printStackTrace(); } finally { if (sqlSession != null) { sqlSession.close(); } } } }
小结
这里的mapper代理方法其实就是加入了一个接口层,这个接口的方法命名有要和id相同,初始参数要和parameterType类型相同,返回值要和resultType相同,这些都是为了我们映射规定的规范。使得更加的灵活。