前言
查询是数据库操作中最频繁,花样最多的操作,没有之一。上一篇文章看来只要在xml里写好sql即可。主要的问题在于各种类型参数如何传参到sql语句里,可能还需要一些动态语句。另外当数据库字段和接收查询结果的实体类属性没对上的话,如何进行映射。
事前准备
上篇文章我们直接在主程序里面进行。这篇文章开始我们使用junit测试。
我是使用Eclipse Oxygen(挺老的版本),Eclipse都是自带junit的,在build path中添加即可,如下图所示:
然后我们在src/test/java目录下添加测试用的类simpleTest.java
package com.sadoshi.test.mybatisTest.test;
import java.io.InputStream;
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.BeforeClass;
import com.sadoshi.test.mybatisTest.mapper.StudentMapper;
public class simpleTest {
private static StudentMapper studentMapper;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
String resource = "mybatis-config.xml";
InputStream inputStream;
try {
inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
studentMapper = (StudentMapper) session.getMapper(StudentMapper.class);
}finally {
}
}
}
其中17-28行内容是用于初始化StudentMapper,这样我们后面写测试方法的时候,直接拿已初始化的StudentMapper即可。
各种查询操作
假设当前我们student数据库的数据如下:
每个测试,我们需要添加的代码都是三个步骤:
1、mapper文件编写接口方法;
2、xml文件里编写查询具体的sql;
3、测试类里调用mapper编写的方法;
单个传入参数的查询
StudentMapper.java中添加selectById方法,指定id为查询条件:
public interface StudentMapper {
//......
public Student selectById(Integer id);
//......
}
StudentMapper.xml中添加以下,其中#{}的内容不一定要和参数的名字一样,可以随意:
<mapper namespace="com.sadoshi.test.mybatisTest.mapper.StudentMapper">
<!-- 省略 -->
<select id="selectById" resultType="com.sadoshi.test.mybatisTest.entity.Student">
select * from student where
id = #{idabcdefg}
</select>
<!-- 省略 -->
</mapper>
测试类simpleTest.java里添加测试方法,这里指定查询id为1的数据:
public class simpleTest {
//......
@Test
public void testSelectById() {
Student res = studentMapper.selectById(1);
System.out.println(res);
}
//......
}
执行后控制台输出结果:
多个传入参数的查询
StudentMapper.java中添加selectByMultiParam方法,指定根据name和age两个参数查询结果。当要传多个参数时,需要通过@Param指定其传参的名字:
public interface StudentMapper {
//......
public List<Student> selectByMultiParam(@Param("name")String name, @Param("age")Integer age);
//......
}
StudentMapper.xml中添加对应的查询条件,其中#{}内的名称要和StudentMapper.java中指定的参数名一致:
<mapper namespace="com.sadoshi.test.mybatisTest.mapper.StudentMapper">
<!-- 省略 -->
<select id="selectByMultiParam" resultType="com.sadoshi.test.mybatisTest.entity.Student">
select * from student
where name like CONCAT('%', #{name}, '%') and age = #{age}
</select>
<!-- 省略 -->
</mapper>
测试类simpleTest.java里添加测试方法,这里指定查询name为李四,age是9的记录:
public class simpleTest {
//......
@Test
public void testSelectByMultiParam() {
List<Student> res =studentMapper.selectByMultiParam("李四", 9);
System.out.println(res);
}
//......
}
执行后控制台输出结果:
传入参数为Map类型的查询
有时候参数可能是通过一个map对象传入,mybatis也是可以接受的。StudentMapper.java添加方法selectByMap,并且给其指定参数名为myMap。其实单传一个map参数的话,不指定也可以,读者可以试试不传的话怎么实现:
public interface StudentMapper {
//......
public List<Student> selectByMap(@Param("myMap")Map<String, Object> map);
//......
}
StudentMapper.xml添加对应的sql:
<mapper namespace="com.sadoshi.test.mybatisTest.mapper.StudentMapper">
<!-- 省略 -->
<select id="selectByMap" resultType="com.sadoshi.test.mybatisTest.entity.Student">
select * from student where
name like CONCAT('%', #{myMap.name}, '%') and age = #{myMap.age}
</select>
<!-- 省略 -->
</mapper>
测试类simpleTest.java里添加测试方法,这里传入name为王五,age为10,查询记录:
public class simpleTest {
//......
@Test
public void testSelectByMap() {
Map<String, Object> map = new HashMap<String, Object>();
map.put("name", "王五");
map.put("age", 10);
List<Student> res =studentMapper.selectByMap(map);
System.out.println(res);
}
//......
}
执行后控制台输出结果:
传入参数为List类型的查询
有时候传参也会是List类型,例如想查询包含了id为1、3的记录,会用到“select ... from .. where ... in (...)” 这样的语法。在in里面通常就是填充我们传进去的List。StudentMapper.java添加selectByList方法,其中参数指定为myList,也可以不指定,只影响在xml里面的引用:
public interface StudentMapper {
//......
public List<Student> selectByList(@Param("myList")List<Integer> list);
//......
}
StudentMapper.xml添加对应的sql。这里要用到动态sql,通过foreach迭代list中的元素,并且以逗号分隔:
<mapper namespace="com.sadoshi.test.mybatisTest.mapper.StudentMapper">
<!-- 省略 -->
<select id="selectByList" resultType="com.sadoshi.test.mybatisTest.entity.Student">
select * from student where id in
<foreach item="item" index="index" collection="myList" open="("
separator="," close=")">
#{item}
</foreach>
</select>
<!-- 省略 -->
</mapper>
测试类simpleTest.java里添加测试方法,这里为list添加1、3两个元素,查询id为1或3的记录:
public class simpleTest {
//......
@Test
public void testSelectByList() {
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(3);
List<Student> res =studentMapper.selectByList(list);
System.out.println(res);
}
//......
}
执行后控制台输出结果:
传入参数为对象类型的查询
一般项目中常见的查询,更多是通过传递对象类型作为参数的。StudentMapper.java中添加selectByObj方法:
public interface StudentMapper {
//......
public List<Student> selectByObj(@Param("obj")Student student);
//......
}
StudentMapper.xml添加对应的sql,看起来和传递map类型有些相似:
<mapper namespace="com.sadoshi.test.mybatisTest.mapper.StudentMapper">
<!-- 省略 -->
<select id="selectByObj" resultType="com.sadoshi.test.mybatisTest.entity.Student">
select * from student where
name like CONCAT('%', #{obj.name}, '%') and age = #{obj.age}
</select>
<!-- 省略 -->
</mapper>
测试类simpleTest.java里添加测试方法:
public class simpleTest {
//......
@Test
public void testSelectByObj() {
Student obj = new Student();
obj.setName("李四");
obj.setAge(9);
List<Student> res =studentMapper.selectByObj(obj);
System.out.println(res);
}
//......
}
执行后控制台输出结果:
动态sql过滤查询项
上面传入参数为对象类型的查询中,我们必须对name和age都赋值,否则对应值为null,查询数据库时就匹配不上了。但实际应用当众,程序员更希望如果不设置对象的某个属性,则查询条件就不包含这个字段,这也是项目中最常见的使用方式。StudentMapper.java中添加selectByCond方法:
public interface StudentMapper {
//......
public List<Student> selectByCond(@Param("obj")Student student);
//......
}
在StudentMapper.xml添加对应的sql,这里通过if进行过滤。注意通过where标签里面,会自动把多余的and去掉,例如当name属性没设置时,where语句只包含"and age = #{obj.age}",这时mybatis会把前面多余的and去掉:
<mapper namespace="com.sadoshi.test.mybatisTest.mapper.StudentMapper">
<!-- 省略 -->
<select id="selectByCond" resultType="com.sadoshi.test.mybatisTest.entity.Student">
select * from student
<where>
<if test="obj.name != '' and obj.name != null">
name like CONCAT('%', #{obj.name}, '%')
</if>
<if test="obj.age != null">
and age = #{obj.age}
</if>
</where>
</select>
<!-- 省略 -->
</mapper>
测试类simpleTest.java里添加测试方法,这里把设置name的代码注释,看看是否会过滤where后面name字段的匹配:
public class simpleTest {
//......
@Test
public void testSelectByCondition() {
Student obj = new Student();
//obj.setName("王五");
obj.setAge(10);
List<Student> res =studentMapper.selectByCond(obj);
System.out.println(res);
}
//......
}
执行后控制台输出结果,果然过滤了name字段的匹配:
关于resultMap的查询
resultMap也是项目中使用比较多的mybatis功能。例如数据表的字段和查询返回结果的对象属性名没有对应,那查询的结果就没法赋予对象了。为了解决这个问题,需要一种映射方式,把数据表的字段和对象的属性对应起来。这就要通过resultMap。StudentMapper.java中添加testResultMap方法:
public interface StudentMapper {
//......
public List<Student2> selectResultMap();
//......
}
在StudentMapper.xml添加对应resultMap,并且需要用到resultMap的sql要把其resultType改为resultMap并指定其id:
<mapper namespace="com.sadoshi.test.mybatisTest.mapper.StudentMapper">
<!-- 省略 -->
<select id="selectResultMap" resultMap="myResultMap">
select * from student
</select>
<resultMap id="myResultMap" type="com.sadoshi.test.mybatisTest.entity.Student2">
<id property="id2" column="id" />
<result property="name2" column="name" />
<result property="age2" column="age" />
</resultMap>
<!-- 省略 -->
</mapper>
上面我们让myResultMap返回类型为Student2,创建Student2类型,所有属性后面都加2,只为了测试resultMap能否把数据表字段映射到Student2的属性上:
package com.sadoshi.test.mybatisTest.entity;
public class Student2 {
private Integer id2;
private String name2;
private Integer age2;
public Integer getId2() {
return id2;
}
public void setId2(Integer id2) {
this.id2 = id2;
}
public String getName2() {
return name2;
}
public void setName2(String name2) {
this.name2 = name2;
}
public Integer getAge2() {
return age2;
}
public void setAge2(Integer age2) {
this.age2 = age2;
}
@Override
public String toString() {
return "Student2 [id2=" + id2 + ", name2=" + name2 + ", age2=" + age2 + "]";
}
}
测试类simpleTest.java里添加测试方法:
public class simpleTest {
//......
@Test
public void testResultMap() {
List<Student2> res = studentMapper.selectResultMap();
System.out.println(res);
}
//......
}
执行后控制台输出结果,成功映射字段:
小结:
本文针对mybatis查询中常见的情况做了一些示例,仅供参考。