Mppaer 动态代理
目录
- 创建 Mapper 工程
- 定义接口的要求
- 测试类
- Mapper 中参数传递
- 单个参数
- 多个参数
- @param命名参数
- 多个参数封装成 Map
- 多个参数之 POJO
- 参数处理源码分析
之前我们一直都使用传统开发模式DAO,即定义接口,然后定义实现类。这个其实是较为繁琐的,MyBatis 中提供了 Mapper 动态代理的方法,操作起来十分简洁,只需要定义满足要求的接口,然后通过 sqlSession 获取到 Mapper对象,便可通过 Mapper 对象调用我们定义的方法。
创建 Mapper 工程
定义接口的要求
-
1、namespace 必须和 Mapper 接口类路径一致。
不使用 Mapper 的话,namespace 的值可以自定义。 -
2、id 必须和 Mapper 接口方法名一致。
不使用 Mapper,使用传统DAO开发模式的话,接口方法名可以自定义。 -
3、parameterType 必须和接口方法参数类型一致。
这个很好理解,传入参数的类型要一样。 -
4、resultType 必须和接口方法返回值类型一致
这个也很好理解,返回值类型要一样。
映射文件 Customer.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="myTest"><!--根据cust_id查询客户--><select id="queryCustomerById" parameterType="Int" resultType="com.itlike.domain.Customer">SELECT * FROM `customer` WHERE cust_id = #{cust_id}</select><!--查询所有的客户,不需要参数--><select id="queryAllCustomer" resultType="com.itlike.domain.Customer">SELECT * FROM `customer`</select><!--根据用户名模糊查询客户--><select id="queryCustomerByName" parameterType="String" resultType="com.itlike.domain.Customer">SELECT * FROM customer WHERE cust_name LIKE '%${value}%';</select><!--添加客户--><insert id="insertCustomer" parameterType="com.itlike.domain.Customer">/*获取插入的最后一个id*/<selectKey keyColumn="cust_id" resultType="Integer" order="AFTER">SELECT last_insert_id()</selectKey>INSERT INTO `customer` (cust_id, cust_name, cust_profession, cust_phone, email)VALUES (#{cust_id}, #{cust_name}, #{cust_profession}, #{cust_phone}, #{email});</insert><!--更新--><update id="updateCustomer" parameterType="com.itlike.domain.Customer">UPDATE `customer` SET cust_name = #{cust_name} WHERE cust_id = #{cust_id}</update><!--删除--><delete id="deleteCustomer" parameterType="com.itlike.domain.Customer">DELETE FROM `customer` WHERE cust_id = #{cust_id}</delete>
</mapper>
Mapper 类:
/*使用 Mapper 动态代理:1.namespace 必须和 Mapper 接口类路径一致2.id 必须和 Mapper 接口方法名一致3.parameterType 必须和接口方法参数类型一致4.resultType 必须和接口方法返回值类型一致
**/
public interface CustomerMapper {// 根据 cust_id 查询客户public Customer queryCustomerById(Integer id);// 查询所有public List<Customer> queryAllCustomer();// 根据用户名模糊查询客户public List<Customer> queryCustomerByName(String name);// 添加客户public void insertCustomer(Customer customer);// 更新public void updateCustomer(Customer customer);// 删除public void deleteCustomer(Customer customer);}
测试类
在 Test 包中 MyTest2.java 中编写一个测试方法:
只要按规范定义好了接口,根据 sqlSession 可以直接获取到 Mapper 对象,可以直接用 Mapper 对象调用接口中的方法。
public void test2(){SqlSession sqlSession = MybatisUtils.openSession();CustomerMapper mapper = sqlSession.getMapper(CustomerMapper.class);List<Customer> customers = mapper.queryAllCustomer();for (Customer customer : customers) {System.out.println(customer);}sqlSession.close();
}
运行结果:成功查询出数据库中所有的数据。
Opening JDBC Connection
Created connection 422392391.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@192d3247]
==> Preparing: SELECT * FROM `customer`
==> Parameters:
<== Columns: cust_id, cust_name, cust_profession, cust_phone, email
<== Row: 1, 鲁班, 射手, 13499887733, 12341241@qq.com
<== Row: 2, 李白, 刺客, 18977665521, libai@163.com
<== Row: 3, 阿轲, 刺客, 18977665997, aike@qq.com
<== Row: 4, 德玛西亚, 肉盾, 13700997665, demaxiya.126.com6
<== Row: 5, 亚索, 战士, 13586878987, yasuo@qq.com
<== Row: 6, 奶妈, 辅助, 13398909089, nama@qq.com
<== Row: 7, 剑圣, 刺客, 13398909088, jiansheng@163.com
<== Row: 8, 盖伦, 肉盾, 15923242231, gailun@126.com
<== Row: 9, 锤石, 辅助, 13398908900, 8888@163.com
<== Row: 11, 孙悟空, 战士, 13728964922, lixin@qq.com
<== Row: 20, 后裔6, null, 18937485936, null
<== Row: 21, 后裔6, null, 18937485936, null
<== Total: 12
Customer{cust_id=1, cust_name='鲁班', cust_profession='射手', cust_phone='13499887733', email='12341241@qq.com'}
Customer{cust_id=2, cust_name='李白', cust_profession='刺客', cust_phone='18977665521', email='libai@163.com'}
Customer{cust_id=3, cust_name='阿轲', cust_profession='刺客', cust_phone='18977665997', email='aike@qq.com'}
Customer{cust_id=4, cust_name='德玛西亚', cust_profession='肉盾', cust_phone='13700997665', email='demaxiya.126.com6'}
Customer{cust_id=5, cust_name='亚索', cust_profession='战士', cust_phone='13586878987', email='yasuo@qq.com'}
Customer{cust_id=6, cust_name='奶妈', cust_profession='辅助', cust_phone='13398909089', email='nama@qq.com'}
Customer{cust_id=7, cust_name='剑圣', cust_profession='刺客', cust_phone='13398909088', email='jiansheng@163.com'}
Customer{cust_id=8, cust_name='盖伦', cust_profession='肉盾', cust_phone='15923242231', email='gailun@126.com'}
Customer{cust_id=9, cust_name='锤石', cust_profession='辅助', cust_phone='13398908900', email='8888@163.com'}
Customer{cust_id=11, cust_name='孙悟空', cust_profession='战士', cust_phone='13728964922', email='lixin@qq.com'}
Customer{cust_id=20, cust_name='后裔6', cust_profession='null', cust_phone='18937485936', email='null'}
Customer{cust_id=21, cust_name='后裔6', cust_profession='null', cust_phone='18937485936', email='null'}
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@192d3247]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@192d3247]
Returned connection 422392391 to pool.
Mapper 中参数传递
单个参数
如果接受的是单个参数,可以接受基本类型,对象类型,集合类型的值。
MyBatis 可直接使用这个参数,不需要经过任何处理。
即,单个参数,参数名字可以自定义。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itlike.mapper.CustomerMapper"><!--根据id查询用户--><select id="getCustomerWithID" parameterType="Integer" resultType="com.itlike.domain.Customer">SELECT * FROM `customer` WHERE cust_id = #{id}</select></mapper>
测试类:
public class MyTest {@Testpublic void test(){SqlSession sqlSession = MybatisUtils.openSession();CustomerMapper mapper = sqlSession.getMapper(CustomerMapper.class);// 查询id为2的用户Customer customer = mapper.getCustomerWithID(2);System.out.println(customer);sqlSession.close();}
}
运行结果:成功查询到id为2的用户
多个参数
多个参数时不需要写 parameterType 。
任意多个参数,都会被 MyBatis 重新包装成一个 Map 传入。
Map 的 key 是 param1,param2,arg0,arg1,值就是参数的值。
即,多个参数,参数名字必须是 param1,param2 或者 arg0, arg1。
以下两种写法都对:
<select id="getCustomerWithID" resultType="com.itlike.domain.Customer">SELECT * FROM `customer` WHERE cust_id = #{arg0} AND cust_name = #{arg1}
</select>
<select id="getCustomerWithID" resultType="com.itlike.domain.Customer">SELECT * FROM `customer` WHERE cust_id = #{param1} AND cust_name = #{param2}
</select>
测试类:
public void test(){SqlSession sqlSession = MybatisUtils.openSession();CustomerMapper mapper = sqlSession.getMapper(CustomerMapper.class);Customer customer = mapper.getCustomerWithID(2, "李白");System.out.println(customer);sqlSession.close();
}
运行结果:
Opening JDBC Connection
Created connection 1122805102.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@42eca56e]
==> Preparing: SELECT * FROM `customer` WHERE cust_id = ? AND cust_name = ?
==> Parameters: 2(Integer), 李白(String)
<== Columns: cust_id, cust_name, cust_profession, cust_phone, email
<== Row: 2, 李白, 刺客, 18977665521, libai@163.com
<== Total: 1
Customer{cust_id=2, cust_name='李白', cust_profession='刺客', cust_phone='18977665521', email='libai@163.com'}
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@42eca56e]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@42eca56e]
Returned connection 1122805102 to pool.
@param命名参数
为参数使用 @Param 起一个名字,
MyBatis 就会将这些参数封装进 Map 中,key 就是我们自己指定的名字。
即,只要在 CustomerMapper 中使用 @Param 就能使用自定义的名字(这个自定义名字只能是 @Param() 括号里写的名字)。
CustomerMapper.java 中:
public interface CustomerMapper {public Customer getCustomerWithID(@Param("id") Integer id, @Param("name") String name);
}
像上面这样,就可以自定义参数名字:
<select id="getCustomerWithID" resultType="com.itlike.domain.Customer">SELECT * FROM `customer` WHERE cust_id = #{id} AND cust_name = #{name}
</select>
测试类:
public void test(){SqlSession sqlSession = MybatisUtils.openSession();CustomerMapper mapper = sqlSession.getMapper(CustomerMapper.class);Customer customer = mapper.getCustomerWithID(2, "李白");System.out.println(customer);sqlSession.close();
}
运行结果:可以正常查询出结果。
注: 如果使用了 @Param 命名参数,param1,param2 作为参数名称依旧可以使用,但是 arg0, arg1 不可用。
多个参数封装成 Map
我们也可以封装多个参数为 Map,直接传递:
在 CustomerMppaer.java 中重载一个参数为 Map 类型的方法:
public interface CustomerMapper {public Customer getCustomerWithID(@Param("id") Integer id, @Param("name") String name);public Customer getCustomerWithID(Map<String, Object> map);
}
测试类:将数据封装到 Map 中,然后将 Map 传入上面重载的方法。
public void test(){SqlSession sqlSession = MybatisUtils.openSession();CustomerMapper mapper = sqlSession.getMapper(CustomerMapper.class);HashMap<String, Object> hashMap = new HashMap<>(); // 将参数封装到 Map 中hashMap.put("id", 1); hashMap.put("name", "鲁班");Customer customer = mapper.getCustomerWithID(hashMap);System.out.println(customer);sqlSession.close();
}
如果通过 Map 传值,在映射文件中的参数名就必须和 Map 的 key 值相同,如下:
<select id="getCustomerWithID" resultType="com.itlike.domain.Customer">SELECT * FROM `customer` WHERE cust_id = #{id} AND cust_name = #{name}
</select>
多个参数之 POJO
我们可以直接传入 POJO 类,但是参数名必须与业务 POJO 中的字段一致。
在 CustomerMppaer.java 中重载一个参数为 POJO 的方法:
public interface CustomerMapper {public Customer getCustomerWithID(@Param("id") Integer id, @Param("name") String name);public Customer getCustomerWithID(Map<String, Object> map);public Customer getCustomerWithID(Customer customer);
}
映射文件 Customer.xml 中参数名必须与POJO中字段一致:
<select id="getCustomerWithID" resultType="com.itlike.domain.Customer">SELECT * FROM `customer` WHERE cust_id = #{cust_id} AND cust_name = #{cust_name}
</select>
测试类:自己创建的对象,必须给它设定好 sql 语句中所需的参数,这里需要给它设置 cust_id 与 cust_name。
public void test(){SqlSession sqlSession = MybatisUtils.openSession();CustomerMapper mapper = sqlSession.getMapper(CustomerMapper.class);Customer customer = new Customer();customer.setCust_name("李白");customer.setCust_id(2);Customer customer1 = mapper.getCustomerWithID(customer);System.out.println(customer1);sqlSession.close();
}
参数处理源码分析
接收参数后,会把参数给放到一个数组当中,如果是一个参数,内部处理时,会自动把该参数返回。
如果是多个参数,内部会做判断,判断是否有 @Param 注解。
如果没有注解的话:就直接使用 arg0,arg1…为 key,放到 map 中,并且还会以 param1 和param2… 为 key 放一份到 map 中。
如果有注解的话: 会使用注解当中的值,替换掉默认的 arg0 和 arg1,使用 @Param 中的值,作为 key 放到一个 map 当中。并且还会以 param1 和 param2… 为 key 放一份到 map 中。
综上,所以传递多个参数时,无论有没有 @Param ,param1、param2 作为参数名都是可以的,当有了 @Param 后,arg0、arg1 便会被替换为 @Param 中的值。