一、Dao 代理实现 CURD
1.案例
1)去掉 Dao 接口实现类
2)getMapper 获取代理对象
只需调用 SqlSession 的 getMapper()方法,即可获取指定接口的实现类对象。该方法的参数为指定 Dao接口类的 class 值。
SqlSession session = factory.openSession(); StudentDao dao = session.getMapper(StudentDao.class); 复制代码
使用工具类:
StudentDao studentDao = MyBatisUtil.getSqlSession().getMapper(StudentDao.class); 复制代码
3)使用 Dao 代理对象方法执行 sql 语句
select 方法:
@Test public void testSelect() throws IOException { final List<Student> studentList = studentDao.selectStudents(); studentList.forEach( stu -> System.out.println(stu)); } 复制代码
insert 方法:
@Test public void testInsert() throws IOException { Student student = new Student(); student.setId(1006); student.setName("林浩"); student.setEmail("linhao@163.com"); student.setAge(26); int nums = studentDao.insertStudent(student); System.out.println("使用 Dao 添加数据:"+nums); } 复制代码
update 方法
@Test public void testUpdate() throws IOException { Student student = new Student(); student.setId(1006); student.setAge(28); int nums = studentDao.updateStudent(student); System.out.println("使用 Dao 修改数据:"+nums); } 复制代码
delete 方法
@Test public void testDelete() throws IOException { int nums = studentDao.deleteStudent(1006); System.out.println("使用 Dao 修改数据:"+nums); } 复制代码
2、原理
动态代理
MapperProxy 类定义:
invoke()方法:
重点方法:
二、深入了解参数
1、parameterType
parameterType: 接口中方法参数的类型, 类型的完全限定名或别名。这个属性是可选的,因为 MyBatis可以推断出具体传入语句的参数,默认值为未设置(unset)。接口中方法的参数从 java 代码传入到mapper 文件的 sql 语句。
int 或 java.lang.Integer
hashmap 或 java.util.HashMap
list 或 java.util.ArrayList
student 或 com.bjpowernode.domain.Student
select,insert,update,delete 都可以使用 parameterType 指定类型。
例如:
<delete id="deleteStudent" parameterType="int"> delete from student where id=#{studentId} </delete> 复制代码
等同于
<delete id="deleteStudent" parameterType="java.lang.Integer"> delete from student where id=#{studentId} </delete> 复制代码
2、Mybatis 传递参数
从 java 代码中把参数传递到 mapper.xml 文件。
3、一个简单参数
Dao 接口中方法的参数只有一个简单类型(java 基本类型和 String),占位符 #{ 任意字符 },和方法的参数名无关。
接口方法:
Student selectById(int id); 复制代码
mapper 文件:
<select id="selectById" resultType="com.bjpowernode.domain.Student"> select id,name,email,age from student where id=#{studentId} </select> 复制代码
#{studentId} , studentId 是自定义的变量名称,和方法参数名无关。
测试方法:
@Test public void testSelectById(){ //一个参数 Student student = studentDao.selectById(1005); System.out.println("查询 id 是 1005 的学生:"+student); } 复制代码
4、多个参数-使用@Param
当 Dao 接口方法多个参数,需要通过名称使用参数。 在方法形参前面加入@Param(“自定义参数名”),mapper 文件使用#{自定义参数名}。
例如定义 List selectStudent( @Param(“personName”) String name ) { … }
mapper 文件 select * from student where name = #{ personName}
接口方法:
List<Student> selectMultiParam(@Param("personName") String name, @Param("personAge") int age); 复制代码
mapper 文件:
<select id="selectMultiParam" resultType="com.bjpowernode.domain.Student"> select id,name,email,age from student where name=#{personName} or age=#{personAge} </select> 复制代码
测试方法:
@Test public void testSelectMultiParam(){ List<Student> stuList = studentDao.selectMultiParam("李力",20); stuList.forEach( stu -> System.out.println(stu)); } 复制代码
5、多个参数-使用对象
使用 java 对象传递参数, java 的属性值就是 sql 需要的参数值。 每一个属性就是一个参数。
语法格式: #{ property,javaType=java 中数据类型名,jdbcType=数据类型名称 }
javaType, jdbcType 的类型 MyBatis 可以检测出来,一般不需要设置。常用格式 #{ property }
创建保存参数值的对象 QueryParam
package com.bjpowernode.vo; public class QueryParam { private String queryName; private int queryAge; //set ,get 方法 } 复制代码
接口方法:
List<Student> selectMultiObject(QueryParam queryParam); 复制代码
mapper 文件:
<select id="selectMultiObject" resultType="com.bjpowernode.domain.Student"> select id,name,email,age from student where name=#{queryName} or age=#{queryAge} </select> 复制代码
或
<select id="selectMultiObject" resultType="com.bjpowernode.domain.Student"> select id,name,email,age from student where name=#{queryName,javaType=string,jdbcType=VARCHAR} or age =#{queryAge,javaType=int,jdbcType=INTEGER} </select> 复制代码
测试方法:
@Test public void selectMultiObject(){ QueryParam qp = new QueryParam(); qp.setQueryName("李力"); qp.setQueryAge(20); List<Student> stuList = studentDao.selectMultiObject(qp); stuList.forEach( stu -> System.out.println(stu)); } 复制代码
6、多个参数-按位置
参数位置从 0 开始, 引用参数语法 #{ arg 位置 } , 第一个参数是#{arg0}, 第二个是#{arg1}
注意:mybatis-3.3 版本和之前的版本使用#{0},#{1}方式, 从 mybatis3.4 开始使用#{arg0}方式。
接口方法:
List<Student> selectByNameAndAge(String name,int age); 复制代码
mapper 文件
<select id="selectByNameAndAge" resultType="com.bjpowernode.domain.Student"> select id,name,email,age from student where name=#{arg0} or age =#{arg1} </select> 复制代码
测试方法:
@Test public void testSelectByNameAndAge(){ //按位置参数 List<Student> stuList = studentDao.selectByNameAndAge("李力",20); stuList.forEach( stu -> System.out.println(stu)); } 复制代码
7、多个参数-使用 Map
Map 集合可以存储多个值,使用 Map 向 mapper 文件一次传入多个参数。Map 集合使用 String 的 key,Object 类型的值存储参数。 mapper 文件使用 # { key } 引用参数值。
例如:
Map<String,Object> data = new HashMap<String,Object>(); data.put(“myname”,”李力”); data.put(“myage”,20); 复制代码
接口方法:
List<Student> selectMultiMap(Map<String,Object> map); 复制代码
mapper 文件:
<select id="selectMultiMap" resultType="com.bjpowernode.domain.Student"> select id,name,email,age from student where name=#{myname} or age =#{myage} </select> 复制代码
测试方法:
@Test public void testSelectMultiMap(){ Map<String,Object> data = new HashMap<>(); data.put("myname","李力");// #{myname} data.put("myage",20); // #{myage} List<Student> stuList = studentDao.selectMultiMap(data); stuList.forEach( stu -> System.out.println(stu)); } 复制代码
8、#和$
#:占位符,告诉 mybatis 使用实际的参数值代替。并使用 PrepareStatement 对象执行 sql 语句, #{…}代替sql 语句的“?”。这样做更安全,更迅速,通常也是首选做法,
mapper 文件
<select id="selectById" resultType="com.bjpowernode.domain.Student"> select id,name,email,age from student where id=#{studentId} </select> 复制代码
转为 MyBatis 的执行是:
String sql=” select id,name,email,age from student where id=?”; PreparedStatement ps = conn.prepareStatement(sql); ps.setInt(1,1005); 复制代码
解释:
- where id=? 就是 where id=#{studentId} ps.setInt(1,1005) , 1005 会替换掉 #{studentId}
- 字符串替换,告诉 mybatis 使用 字 符 串 替 换 , 告 诉 m y b a t i s 使 用 包含的“字符串”替换所在位置。使用 Statement 把 sql 语句和${}的内容连接起来。主要用在替换表名,列名,不同列排序等操作。
例 1: 分别使用 id , email 列查询 Student
接口方法:
Student findById(int id); Student findByEmail(String email); 复制代码
mapper 文件:
<select id="findById" resultType="com.bjpowernode.domain.Student"> select * from student where id=#{studentId} </select> <select id="findByEmail" resultType="com.bjpowernode.domain.Student"> select * from student where email=#{stuentEmail} </select> 复制代码
测试方法:
@Test public void testFindStuent(){ Student student1 = studentDao.findById(1002); System.out.println("findById:"+student1); Student student2 = studentDao.findByEmail("zhou@126.net"); System.out.println("findByEmail:"+student2); } 复制代码
例 2:通用方法,使用不同列作为查询条件
接口方法:
Student findByDiffField(@Param("col") String colunName,@Param("cval") Object value); 复制代码
mapper 文件:
<select id="findByDiffField" resultType="com.bjpowernode.domain.Student"> select * from student where ${col} = #{cval} </select> 复制代码
测试方法:
@Test public void testFindDiffField(){ Student student1 = studentDao.findByDiffField("id",1002); System.out.println("按 id 列查询:"+student1); Student student2 = studentDao.findByDiffField("email","zhou@126.net"); System.out.println("按 email 列查询:"+student2); } 复制代码
三、封装 MyBatis 输出结果
1、resultType
resultType: 执行 sql 得到 ResultSet 转换的类型,使用类型的完全限定名或别名。 注意如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身。resultType 和 resultMap,不能同时使用。
3)简单类型
接口方法:
int countStudent(); 复制代码
mapper 文件:
<select id="countStudent" resultType="int"> select count(*) from student </select> 复制代码
测试方法:
@Test public void testRetunInt(){ int count = studentDao.countStudent(); System.out.println("学生总人数:"+ count); } 复制代码
2) 对象类型
接口方法:
Student selectById(int id); 复制代码
mapper 文件:
<select id="selectById" resultType="com.bjpowernode.domain.Student"> select id,name,email,age from student where id=#{studentId} </select> 复制代码
框架的处理: 使用构造方法创建对象。调用 setXXX 给属性赋值。 Student student = new Student();
注意:Dao 接口方法返回是集合类型,需要指定集合中的类型,不是集合本身。
3)Map
sql 的查询结果作为 Map 的 key 和 value。推荐使用 Map<Object,Object>。 注意:Map 作为接口返回值,sql 语句的查询结果最多只能有一条记录。大于一条记录是错误。
接口方法:
Map<Object,Object> selectReturnMap(int id); 复制代码
mapper 文件:
<select id="selectReturnMap" resultType="java.util.HashMap"> select name,email from student where id = #{studentId} </select> 复制代码
测试方法:
@Test public void testReturnMap(){ Map<Object,Object> retMap = studentDao.selectReturnMap(1002); System.out.println("查询结果是 Map:"+retMap); } 复制代码
2、resultMap
resultMap 可以自定义 sql 的结果和 java 对象属性的映射关系。更灵活的把列值赋值给指定属性。常用在列名和 java 对象属性名不一样的情况。
使用方式:
- 1.先定义 resultMap,指定列名和属性的对应关系。
- 2.在select中把 resultType 替换为 resultMap。
接口方法:
List<Student> selectUseResultMap(QueryParam param); 复制代码
mapper 文件:
<!-- 创建 resultMap id:自定义的唯一名称,在<select>使用 type:期望转为的 java 对象的全限定名称或别名 --> <resultMap id="studentMap" type="com.bjpowernode.domain.Student"> <!-- 主键字段使用 id --> <id column="id" property="id" /> <!--非主键字段使用 result--> <result column="name" property="name"/> <result column="email" property="email" /> <result column="age" property="age" /> </resultMap> <!--resultMap: resultMap 标签中的 id 属性值--> <select id="selectUseResultMap" resultMap="studentMap"> select id,name,email,age from student where name=#{queryName} orage=#{queryAge} </select> 复制代码
测试方法:
@Test public void testSelectUseResultMap(){ QueryParam param = new QueryParam(); param.setQueryName("李力"); param.setQueryAge(20); List<Student> stuList = studentDao.selectUseResultMap(param); stuList.forEach( stu -> System.out.println(stu)); } 复制代码
3、实体类属性名和列名不同的处理方式
1)使用列别名和
步骤:
- 创建新的实体类 PrimaryStudent
package com.bjpowernode.domain; /** * <p>Description: 实体类 </p> * <p>Company: http://www.bjpowernode.com */ public class PrimaryStudent { private Integer stuId; private String stuName; private Integer stuAge; // set , get 方法 } 复制代码
- 接口方法
List<PrimaryStudent> selectUseFieldAlias(QueryParam param); 复制代码
- mapper 文件:
<select id="selectUseFieldAlias" resultType="com.bjpowernode.domain.PrimaryStudent"> select id as stuId, name as stuName,age as stuAge from student where name=#{queryName} or age=#{queryAge} </select> 复制代码
4.测试方法
@Test public void testSelectUseFieldAlias(){ QueryParam param = new QueryParam(); param.setQueryName("李力"); param.setQueryAge(20); List<PrimaryStudent> stuList; stuList = studentDao.selectUseFieldAlias(param); stuList.forEach( stu -> System.out.println(stu)); } 复制代码
2)使用
步骤:
- 接口方法
List<PrimaryStudent> selectUseDiffResultMap(QueryParam param); 复制代码
- mapper 文件:
<!-- 创建 resultMap id:自定义的唯一名称,在<select>使用 type:期望转为的 java 对象的全限定名称或别名 --> <resultMap id="primaryStudentMap" type="com.bjpowernode.domain.PrimaryStudent"> <!-- 主键字段使用 id --> <id column="id" property="stuId" /> <!--非主键字段使用 result--> <result column="name" property="stuName"/> <result column="age" property="stuAge" /> </resultMap> <!--resultMap: resultMap 标签中的 id 属性值--> <select id="selectUseDiffResultMap" resultMap="primaryStudentMap"> select id,name,email,age from student where name=#{queryName} or age=#{queryAge} </select> 复制代码
- 测试方法
@Test public void testSelectUseDiffResultMap(){ QueryParam param = new QueryParam(); param.setQueryName("李力"); param.setQueryAge(20); List<PrimaryStudent> stuList; stuList = studentDao.selectUseDiffResultMap(param); stuList.forEach( stu -> System.out.println(stu)); } 复制代码
四、模糊 like
模糊查询的实现有两种方式, 一是 java 代码中给查询数据加上“%” ; 二是在 mapper 文件 sql 语句的条件位置加上“%”
需求:查询姓名有“力”的
例 1: java 代码中提供要查询的 “%力%”
接口方法:
List<Student> selectLikeFirst(String name); 复制代码
mapper 文件:
<select id="selectLikeFirst" resultType="com.bjpowernode.domain.Student"> select id,name,email,age from student where name like #{studentName} </select> 复制代码
测试方法:
@Test public void testSelectLikeOne(){ String name="%力%"; List<Student> stuList = studentDao.selectLikeFirst(name); stuList.forEach( stu -> System.out.println(stu)); } 复制代码
例 2:mapper 文件中使用 like name "%" #{xxx} "%"
接口方法:
List<Student> selectLikeSecond(String name); 复制代码
mapper 文件:
<select id="selectLikeSecond" resultType="com.bjpowernode.domain.Student"> select id,name,email,age from student where name like "%" #{studentName} "%" </select> 复制代码
测试方法:
@Test public void testSelectLikeSecond(){ String name="力"; List<Student> stuList = studentDao.selectLikeSecond(name); stuList.forEach( stu -> System.out.println(stu)); } 复制代码