8.4. 基本查询

8.4.1. 返回单条数据

先从最简单的开始,执行返回单条数据的查询。在上次课的例子中的映射文件添加一个映射,查询单条记录:

  1. <!--namespace属性必须要有,给mapper起个名字-->
  2. <mapper namespace="org.hrbust.tmall.mapper.CustomerMapper">
  3. <select id="getCustomerById" resultType="org.hrbust.tmall.entity.Customer">
  4. select * from Customer where id=1
  5. </select>
  6. </mapper>

说明:

  • <mapper>是XML映射文件的根标签,它的namespace属性不能省略,相当于给这个映射起一个名字,我们习惯于一个实体类对应一个映射,将这个实体类的数据库操作都写到这个映射里。这个namespace属性还有其它用途,遇到时候再说。
  • <select>子标签用于定义一个查询操作,并将结果映射到实体类上。id属性是它的名字,用于引用这个操作。resultType指明映射后的实体类名,MyBatis依据这个类型完成映射。注意这里要用完全限定名。(当然也可以在主配置文件中设置它的别名)
  • <select>标签的内容就是用于查询的SQL语句,MyBatis的一大特色就是让用户自己写SQL语句,这样便于优化。

这个查询配置完以后,就可以通过它执行数据库操作了,还是上次课的例子:

  1. public class Main {
  2. public static void main(String args[]) {
  3. Customer customer = null;
  4. SqlSession session = null;
  5. try {
  6. InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
  7. SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
  8. session = sqlSessionFactory.openSession();
  9. customer = session.selectOne("getCustomer");
  10. }catch (Exception ex){
  11. } finally {
  12. session.close();
  13. }
  14. System.out.println(customer);
  15. }
  16. }

说明:

  • SqlSession的selectOne()方法用于执行具有一条返回值的查询,返回类型是泛型T,因此不用进行强制类型转换。
  • SqlSession使用完毕后不要忘记close()

8.4.2. 带参数的查询

还是这个例子,这次带个参数:

  1. <!--namespace属性必须要有,给mapper起个名字-->
  2. <mapper namespace="org.hrbust.tmall.mapper.CustomerMapper">
  3. <select id="getCustomerById" parameterType="int" resultType="org.hrbust.tmall.entity.Customer">
  4. select * from Customer where id=#{id}
  5. </select>
  6. </mapper>

说明:

  • parameterType=”int”指明参数的类型,此处是int,也可以写Integer。这个参数类型其实也可以不写,不写的话MyBatis会推测类型,但写了就一定要符合,相当于给一个强类型支持。当然还是写得好,这样能把错误限定在代码里,不会带入DBMS。
  • 注意参数的写法#{},这是MyBatis定义的特殊标记,遇到他会在SQL语句中生成类似where id=?这样的语句,#{id}里面的id可以取任意的名字
  • 相应的selectOne()函数要这么用:selectOne(“getCustomerById”, 1),其中第二个参数是Object类型

当查询参数多余一个的时候,可以参数封装到一个Map里:

  1. <!--namespace属性必须要有,给mapper起个名字-->
  2. <mapper namespace="org.hrbust.tmall.mapper.CustomerMapper">
  3. <select id="getCustomerById" parameterType="java.util.Map" resultType="org.hrbust.tmall.entity.Customer">
  4. select * from Customer where id=#{id} and name like #{name}
  5. </select>
  6. </mapper>

查询时:

  1. Map<String, Object> params = new HashMap<>();
  2. params.put("id", 2);
  3. params.put("name", "张%");
  4. Customer customer = session.selectOne("getCustomerById", params);

需要注意的几个问题:

  • Map的第二个泛型类型是Object,因为参数可能是任何类型
  • 同样parameterType可以不写,MyBatis会自动推断类型
  • 此时Map的key值要与#{}里面的名字对应,否则参数传不进去
  • 这个方法很笨,后面有更好的方法

8.4.3. 返回集合

很多情况下查询会返回一个集合,对应的映射后的类型就是实体类的集合,下面这个查询我们是熟悉的:

  1. <select id="getCustomers" resultType="org.hrbust.tmall.entity.Customer">
  2. select * from Customer
  3. </select>

查询时使用SqlSession的selectList()方法:

  1. List<Customer> customers = session.selectList("getCustomers");

说明

  • <select>的resultType属性还是实体类型,MyBatis会自动将返回的数据映射成相应类型的集合
  • 查询使用SqlSession的selectList()方法,返回的是泛型List,不用进行强制类型转换

另外,也可以将结果映射成Map,使用SqlSession的selectMap()方法:

  1. Map<String, Customer> customers = session.selectMap("getCustomers", "id"); // "id"指明使用哪个列作为Map的key

说明:

  • selectMap()的第二个参数用于指明使用哪个列作为Map的key

8.4.4. resultMap

在前面的例子中有一个问题,MyBatis怎么知道数据库表的哪个列对应实体类的哪个属性?靠的就是resultMap,他定义了列和属性的对应关系。那么之前为什么没见到这个resultMap呢?因为我们的数据库字段名和实体类的属性名是相同的,此时MyBatis会维护一个默认的resultMap,会将同名的列和属性对应起来,并忽略大小写。当然我们也可以显式地配置resultMap,认为地使列名和属性名对应起来。

  1. <mapper namespace="org.hrbust.tmall.mapper.CustomerMapper">
  2. <!--这里使用resultMap,不再使用resultType-->
  3. <select id="getCustomers" resultMap="customerResultMap">
  4. select * from Customer
  5. </select>
  6. <resultMap id="customerResultMap" type="org.hrbust.tmall.entity.Customer">
  7. <id property="id" column="id" />
  8. <result property="name" column="name" />
  9. <result property="mobilePhone" column="mobilePhone" />
  10. <result property="birthDate" column="birthDate" />
  11. </resultMap>
  12. </mapper>

说明:

  • <resultMap>的id定义了这个结果映射的唯一标识,type指明实体类型。
  • 每一个<id><result>用于映射一个列到一个属性,只能是字面值,当然这个例子里property和column是相同的。
  • <id><result>的区别是,_<id>_元素对应的属性会被标记为对象的标识符,在比较对象实例时使用。 这样可以提高整体的性能,尤其是进行缓存和嵌套结果映射(也就是连接映射)的时候。在某些时候,不使用_<id>_可能会出错
  • property对应实体类的属性,这里可以使用级联属性,如cart.totalPrice,后面学习关联查询时会用到。
  • 数据类型一般不用指定,在映射到实体类上时MyBatis会自动推断,但是映射到Map上时就需要指定了。
  • 原理就是依赖注入,上面的配置是基于setter的注入,MyBatis也支持构造函数注入,具体请查看官方文档。

看起来好像没那么有必要使用resultMap,那是因为我们之前的查询太简单了,比如,Customer的cart属性和promotions属性都没有查询出来。如果要进行关联查询,就必须要使用resultMap了。