8.4. 基本查询
8.4.1. 返回单条数据
先从最简单的开始,执行返回单条数据的查询。在上次课的例子中的映射文件添加一个映射,查询单条记录:
<!--namespace属性必须要有,给mapper起个名字--><mapper namespace="org.hrbust.tmall.mapper.CustomerMapper"><select id="getCustomerById" resultType="org.hrbust.tmall.entity.Customer">select * from Customer where id=1</select></mapper>
说明:
<mapper>是XML映射文件的根标签,它的namespace属性不能省略,相当于给这个映射起一个名字,我们习惯于一个实体类对应一个映射,将这个实体类的数据库操作都写到这个映射里。这个namespace属性还有其它用途,遇到时候再说。<select>子标签用于定义一个查询操作,并将结果映射到实体类上。id属性是它的名字,用于引用这个操作。resultType指明映射后的实体类名,MyBatis依据这个类型完成映射。注意这里要用完全限定名。(当然也可以在主配置文件中设置它的别名)<select>标签的内容就是用于查询的SQL语句,MyBatis的一大特色就是让用户自己写SQL语句,这样便于优化。
这个查询配置完以后,就可以通过它执行数据库操作了,还是上次课的例子:
public class Main {public static void main(String args[]) {Customer customer = null;SqlSession session = null;try {InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);session = sqlSessionFactory.openSession();customer = session.selectOne("getCustomer");}catch (Exception ex){} finally {session.close();}System.out.println(customer);}}
说明:
- SqlSession的selectOne()方法用于执行具有一条返回值的查询,返回类型是泛型T,因此不用进行强制类型转换。
- SqlSession使用完毕后不要忘记close()
8.4.2. 带参数的查询
还是这个例子,这次带个参数:
<!--namespace属性必须要有,给mapper起个名字--><mapper namespace="org.hrbust.tmall.mapper.CustomerMapper"><select id="getCustomerById" parameterType="int" resultType="org.hrbust.tmall.entity.Customer">select * from Customer where id=#{id}</select></mapper>
说明:
- parameterType=”int”指明参数的类型,此处是int,也可以写Integer。这个参数类型其实也可以不写,不写的话MyBatis会推测类型,但写了就一定要符合,相当于给一个强类型支持。当然还是写得好,这样能把错误限定在代码里,不会带入DBMS。
- 注意参数的写法
#{},这是MyBatis定义的特殊标记,遇到他会在SQL语句中生成类似where id=?这样的语句,#{id}里面的id可以取任意的名字 - 相应的selectOne()函数要这么用:selectOne(“getCustomerById”, 1),其中第二个参数是Object类型
当查询参数多余一个的时候,可以参数封装到一个Map里:
<!--namespace属性必须要有,给mapper起个名字--><mapper namespace="org.hrbust.tmall.mapper.CustomerMapper"><select id="getCustomerById" parameterType="java.util.Map" resultType="org.hrbust.tmall.entity.Customer">select * from Customer where id=#{id} and name like #{name}</select></mapper>
查询时:
Map<String, Object> params = new HashMap<>();params.put("id", 2);params.put("name", "张%");Customer customer = session.selectOne("getCustomerById", params);
需要注意的几个问题:
- Map的第二个泛型类型是Object,因为参数可能是任何类型
- 同样parameterType可以不写,MyBatis会自动推断类型
- 此时Map的key值要与
#{}里面的名字对应,否则参数传不进去 - 这个方法很笨,后面有更好的方法
8.4.3. 返回集合
很多情况下查询会返回一个集合,对应的映射后的类型就是实体类的集合,下面这个查询我们是熟悉的:
<select id="getCustomers" resultType="org.hrbust.tmall.entity.Customer">select * from Customer</select>
查询时使用SqlSession的selectList()方法:
List<Customer> customers = session.selectList("getCustomers");
说明
<select>的resultType属性还是实体类型,MyBatis会自动将返回的数据映射成相应类型的集合- 查询使用SqlSession的selectList()方法,返回的是泛型List,不用进行强制类型转换
另外,也可以将结果映射成Map,使用SqlSession的selectMap()方法:
Map<String, Customer> customers = session.selectMap("getCustomers", "id"); // "id"指明使用哪个列作为Map的key
说明:
- selectMap()的第二个参数用于指明使用哪个列作为Map的key
8.4.4. resultMap
在前面的例子中有一个问题,MyBatis怎么知道数据库表的哪个列对应实体类的哪个属性?靠的就是resultMap,他定义了列和属性的对应关系。那么之前为什么没见到这个resultMap呢?因为我们的数据库字段名和实体类的属性名是相同的,此时MyBatis会维护一个默认的resultMap,会将同名的列和属性对应起来,并忽略大小写。当然我们也可以显式地配置resultMap,认为地使列名和属性名对应起来。
<mapper namespace="org.hrbust.tmall.mapper.CustomerMapper"><!--这里使用resultMap,不再使用resultType--><select id="getCustomers" resultMap="customerResultMap">select * from Customer</select><resultMap id="customerResultMap" type="org.hrbust.tmall.entity.Customer"><id property="id" column="id" /><result property="name" column="name" /><result property="mobilePhone" column="mobilePhone" /><result property="birthDate" column="birthDate" /></resultMap></mapper>
说明:
<resultMap>的id定义了这个结果映射的唯一标识,type指明实体类型。- 每一个
<id>或<result>用于映射一个列到一个属性,只能是字面值,当然这个例子里property和column是相同的。 <id>和<result>的区别是,_<id>_元素对应的属性会被标记为对象的标识符,在比较对象实例时使用。 这样可以提高整体的性能,尤其是进行缓存和嵌套结果映射(也就是连接映射)的时候。在某些时候,不使用_<id>_可能会出错。- property对应实体类的属性,这里可以使用级联属性,如
cart.totalPrice,后面学习关联查询时会用到。 - 数据类型一般不用指定,在映射到实体类上时MyBatis会自动推断,但是映射到Map上时就需要指定了。
- 原理就是依赖注入,上面的配置是基于setter的注入,MyBatis也支持构造函数注入,具体请查看官方文档。
看起来好像没那么有必要使用resultMap,那是因为我们之前的查询太简单了,比如,Customer的cart属性和promotions属性都没有查询出来。如果要进行关联查询,就必须要使用resultMap了。
