Mybatis中原生DAO实现和Mapper动态代理实现
Mybatis开发dao的方法通常用两种,一种是传统DAO的方法,另一种是基于mapper代理的方法。
一、传统DAO方式开发
1、sql语句映射文件编写
User.xml
<?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="test"> <!-- id:sql语句唯一标识 parameterType:指定传入参数类型(pojo类中对应的类型,不是数据库中的类型) resultType:返回结果集类型 #{}:占位符,如果传入的类型是基本类型(string,long,double,int,boolean,float等),那么#{}中变量名称可以任意 --> <select id="findUserById" parameterType="java.lang.Integer" resultType="cn.itheima.pojo.User"> SELECT * FROM user WHERE id=#{id} </select> <!-- 如果返回的结果为集合,resultType中也是配置为集合中泛型的类型,即resultType="cn.itheima.pojo.User" ${}:拼接符,如果传入的类型是基本类型(string,long,double,int,boolean,float等),那么${}中变量名称必须是value --> <select id="findUserByUserName" parameterType="java.lang.String" resultType="cn.itheima.pojo.User"> SELECT * FROM user WHERE username LIKE '%${value}%' </select> <!-- 如果传入的是pojo类型,则#{}中变量名称必须是pojo中对应的属性.属性.属性...... 如果要返回数据库自增主键,可以使用SELECT LAST_INSERT_ID() --> <insert id="insertUser" parameterType="cn.itheima.pojo.User"> <!-- 执行SELECT LAST_INSERT_ID()数据库函数,返回自增的主键 keyProperty:将返回的主键放入传入的参数的Id中保存(保存到user对象中的id属性) order:当前函数相对于insert语句的执行顺序,在insert前执行用BEFORE,在insert后执行用AFTER resultType:id的类型,也就是keyProperty中属性类型 --> <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="deleteUserById" parameterType="java.lang.Integer"> DELETE FROM user WHERE id=#{id} </delete> <update id="updateUserById" parameterType="cn.itheima.pojo.User"> UPDATE user SET username=#{username} WHERE id=#{id} </update> </mapper>
2、配置User.xml映射信息
在Sqlconfig.xml文件中引入User.xml
<mappers> <mapper resource="User.xml"/> </mappers>
3、编写DAO接口和实现类
UserDAO.java
package cn.itheima.dao; import java.util.List; import cn.itheima.pojo.User; public interface UserDAO { User findUserById(Integer id); List<User> findUserByUserName(String username); }
UserDAOImpl.java
package cn.itheima.dao; import java.util.List; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import cn.itheima.pojo.User; public class UserDAOImpl implements UserDAO { private SqlSessionFactory sqlSessionFactory; // 通过构造方法注入 public UserDAOImpl(SqlSessionFactory sqlSessionFactory) { this.sqlSessionFactory = sqlSessionFactory; } @Override public User findUserById(Integer id) { // SqlSession是线程不安全的,所以最佳使用范围是方法里 SqlSession session = sqlSessionFactory.openSession(); User user = session.selectOne("test.findUserById", id); return user; } @Override public List<User> findUserByUserName(String username) { SqlSession session = sqlSessionFactory.openSession(); List<User> userList = session.selectList("test.findUserByUserName", username); return userList; } }
4、测试
package mybatis0523; import java.io.InputStream; import java.util.List; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Before; import org.junit.Test; import cn.itheima.dao.UserDAO; import cn.itheima.dao.UserDAOImpl; import cn.itheima.pojo.User; public class UserDAOTest { private SqlSessionFactory sqlSessionFactory; // 在测试方法前执行的方法 @Before public void setUp() throws Exception { String resource = "sqlMapConfig.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } @Test public void testFindUserById() throws Exception { // 将初始化好的sqlSessionFactory注入到实现类中 UserDAO userDAO = new UserDAOImpl(sqlSessionFactory); User user = userDAO.findUserById(1); System.out.println(user); } @Test public void testFindUserByUserName() throws Exception { UserDAO userDAO = new UserDAOImpl(sqlSessionFactory); List<User> userList = userDAO.findUserByUserName("王"); System.out.println(userList); } }
二、Mapper代理方式开发
1、实现原理
Mapper接口开发方法只需要编写Mapper接口(实际上相当于DAO接口)然后由Mybatis根据接口定义创建接口的动态代理对象。换句话说也就是不用自己编写DAO接口的实现类了。
2、Map接口编写规则
(1)映射文件中的namespace要等于接口的全路径
(2)映射文件中的sql语句id要等于接口的方法名称
(3)映射文件中传入参数类型要等于接口方法中传入参数的类型
(4)映射文件中返回结果集类型要等于接口方法的返回值类型
3、编写Mapper接口(相当与传统的DAO接口)
package cn.itheima.mapper; import java.util.List; import cn.itheima.pojo.User; public interface UserMapper { User findUserById(Integer id); List<User> findUserByUserName(String username); void insertUser(User user); void updateUserById(User user); }
4、编写映射文件
UserMapper.xml
<?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接口代理实现编写规则: 1、映射文件中的namespace要等于接口的全路径 2、映射文件中的sql语句id要等于接口的方法名称 3、映射文件中传入参数类型要等于接口方法中传入参数的类型 4、映射文件中返回结果集类型要等于接口方法的返回值类型 --> <mapper namespace="cn.itheima.mapper.UserMapper"> <!-- id:sql语句唯一标识 parameterType:指定传入参数类型(pojo类中对应的类型,不是数据库中的类型) resultType:返回结果集类型 #{}:占位符,如果传入的类型是基本类型(string,long,double,int,boolean,float等),那么#{}中变量名称可以任意 --> <select id="findUserById" parameterType="java.lang.Integer" resultType="cn.itheima.pojo.User"> SELECT * FROM user WHERE id=#{id} </select> <!-- 如果返回的结果为集合,resultType中也是配置为集合中泛型的类型,即resultType="cn.itheima.pojo.User" ${}:拼接符,如果传入的类型是基本类型(string,long,double,int,boolean,float等),那么${}中变量名称必须是value --> <select id="findUserByUserName" parameterType="java.lang.String" resultType="cn.itheima.pojo.User"> SELECT * FROM user WHERE username LIKE '%${value}%' </select> <!-- 如果传入的是pojo类型,则#{}中变量名称必须是pojo中对应的属性.属性.属性...... 如果要返回数据库自增主键,可以使用SELECT LAST_INSERT_ID() --> <insert id="insertUser" parameterType="cn.itheima.pojo.User"> <!-- 执行SELECT LAST_INSERT_ID()数据库函数,返回自增的主键 keyProperty:将返回的主键放入传入的参数的Id中保存(保存到user对象中的id属性) order:当前函数相对于insert语句的执行顺序,在insert前执行用BEFORE,在insert后执行用AFTER resultType:id的类型,也就是keyProperty中属性类型 --> <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="deleteUserById" parameterType="java.lang.Integer"> DELETE FROM user WHERE id=#{id} </delete> <update id="updateUserById" parameterType="cn.itheima.pojo.User"> UPDATE user SET username=#{username} WHERE id=#{id} </update> </mapper>
5、配置UserMapper.xml映射信息
在SqlConfig.xml中引入
<mappers> <!-- 使用class属性引入接口的全路径名称: 使用规则: 1、接口的名称和映射文件名称要完全一致 2、接口和映射文件要放在同一个目录下 --> <mapper class="cn.itheima.mapper.UserMapper"/> </mappers>
6、测试
package mybatis0523; import java.io.InputStream; 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 org.junit.Before; import org.junit.Test; import cn.itheima.mapper.UserMapper; import cn.itheima.pojo.User; public class UserMapperTest { private SqlSessionFactory sqlSessionFactory; // 在测试方法前执行的方法 @Before public void setUp() throws Exception { String resource = "sqlMapConfig.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } @Test public void testFindUserById() throws Exception { SqlSession session = sqlSessionFactory.openSession(); UserMapper userMapper = session.getMapper(UserMapper.class); User user = userMapper.findUserById(1); System.out.println(user); } @Test public void testFindUserByUserName() throws Exception { SqlSession session = sqlSessionFactory.openSession(); UserMapper userMapper = session.getMapper(UserMapper.class); List<User> userList = userMapper.findUserByUserName("王"); System.out.println(userList); } }
三、总结
原始DAO开发存在一些问题:
1、存在一定量的模板代码。比如:通过SqlSessionFactory创建SqlSession;调用SqlSession的方法操作数据库;关闭Sqlsession。
2、存在一些硬编码。调用SqlSession的方法操作数据库时,需要指定statement的id,这里存在了硬编码。
Mapper代理的开发方式,只需要编写mapper接口(相当于dao接口)即可。Mybatis会自动的为mapper接口生成动态代理实现类。
要实现mapper代理的开发方式,需要遵循一些开发规范:
1、mapper接口的全限定名要和mapper映射文件的namespace的值相同。
2、mapper接口的方法名称要和mapper映射文件中的statement的id相同。
3、mapper接口的方法参数只能有一个,且类型要和mapper映射文件中statement的parameterType的值保持一致。
4、 mapper接口的返回值类型要和mapper映射文件中statement的resultType值或resultMap中的type值保持一致。
通过规范式的开发mapper接口,可以解决原始dao开发当中存在的问题:
1、 模板代码已经去掉。
2、 剩下去不掉的操作数据库的代码,其实就是一行代码。这行代码中硬编码的部分,通过第一和第二个规范就可以解决。