在上一章中我们在 mybatis-config.xml 中配置了 StudentMapper.xml 文件的位置,下面我们以 StudentMapper.xml 为例,详细讨论myBatis 中 XxxMapper.xml 的使用
假设我们现有一个StudentMapper.xml文件,里面有如下内容:
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.briup.mappers.StudentMapper"> <select id="findStudentById" parameterType="int" resultType="Student"> select stud_id as studId, name, email, dob from students where stud_id=#{studId} </select> </mapper>
在StudentMapper.xml文件中,所有标签都配置在 <mapper>
标签下,通过 <mapper>
标签中通过namespace属性指定StudentMapper.xml的映射文件的位置。
在将来后端调用sql语句的时候有两种调用方式:
1)通过字符串,调用映射文件中的SQL语句,字符串格式为:映射文件的 namespace + sql语句的id
例如:
SqlSession sqlSession = MyBatisSqlSessionFactory.openSession(); try{ Student student = sqlSession.selectOne("com.briup.mappers.StudentMapper.findStudentById", 1); System.out.println(student); } finally { sqlSession.close(); }
这种方式容易出错,因为需要自己编写字符串,我们需要检查映射文件中namespace,以及sql语句定义中对参数和返回值的要求,以保证输入的参数类型和结果返回类型是有效的。
注意,使用这种字符串的形式来调用sql语句,有没有接口映射XxxMapper.xml都无所谓了,即使接口中没有对应的方法,也不会影响sql语句的执行
2)MyBatis中可以通过使用映射接口Mapper,调用映射文件中的sql。
注意:
sql映射文件中的namespace 和 映射接口的全限定名要保持一致。 sql映射文件中的sql语句id值 和 映射接口中的方法名要保持一致。 sql语句配置的parameterType属性 和 映射接口中对应的方法的参数类型保持一致。 sql语句配置的returnType属性 和 映射接口中对应的方法的返回值类型保持一致。
例如:映射接口StudentMapper.java
package com.briup.mappers; public interface StudentMapper{ Student findStudentById(Integer id); }
通过映射接口,调用映射文件中的SQL语句。
代码如下:
SqlSession sqlSession = MyBatisSqlSessionFactory.openSession(); try { StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class); Student stu = studentMapper.findStudentById(1); } finally { sqlSession.close(); }
MyBatis提供了不同的标签来配置不同类型的sql语句,如:SELECT,INSERT,UPDATE,DELETE。
一个INSERT语句可以在 <insert>
标签元素中进行配置,如下所示:
<insert id="insertStudent" parameterType="Student"> INSERT INTO STUDENTS(STUD_ID,NAME,EMAIL,PHONE) VALUES(#{studId},#{name},#{email},#{phone}) </insert>
ID属性为insertStudent,可以在当前xml文件中的命名空间:`com.briup.mappers.StudentMapper.insertStudent`中唯一标识该sql语句。 parameterType 属性是一个完全限定类名或者是一个类型别名alias。
可以如下调用这个sql语句:
int count = sqlSession.insert("com.briup.mappers.StudentMapper.insertStudent", student);
sqlSession.insert() 方法返回执行 INSERT 语句后所影响的行数,或者使用映射接口Mapper来调用:
public interface Student Mapper{ int insertStudent(Student student); } StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); int count = mapper.insertStudent(student);
自动生成主键:
在INSERT语句中,可以自动生成主键列 STUD_ID 的值。
使用 useGeneratedKeys
和 keyProperty
属性让数据库生成 auto_increment
列的值,并将生成的值设置到其中一个输入对象属性内,
如下所示:
<insert id="insertStudent" parameterType="Student" useGeneratedKeys="true" keyProperty="studId"> INSERT INTO STUDENTS(NAME, EMAIL, PHONE) VALUES(#{name},#{email},#{phone}) </insert>
这里STUD_ID列值将会被数据库自动生成(如mysql),并且生成的值会被设置到student对象的studId属性上。
注意:有些数据库,如oracle,并不支持AUTO_INCREMENT列,但是oracle中可以使用序列来生成主键值。
例如:使用序列my_seq来生成SUTD_ID主键值。使用如下代码来生成主键:
drop sequence my_seq; create sequence my_seq; <insert id="insertStudent" parameterType="Student"> <selectKey keyProperty="studId" resultType="int" order="BEFORE"> SELECT my_seq.nextval FROM DUAL </selectKey> INSERT INTO STUDENTS(STUD_ID,NAME,EMAIL, PHONE) VALUES(#{studId},#{name},#{email},#{phone}) </insert>
这里使用了 <selectKey>
标签来获取主键值,并将值保存到Student对象的studId 属性上。
属性 order="before"
表示:MyBatis将取得序列的下一个值作为主键值,并且在执行INSERT语句之前将值设置到studId属性上。
一个UPDATE语句可以在<update>标签元素中进行配置,如下所示:
<update id="updateStudent" parameterType="Student"> UPDATE STUDENTS SET NAME=#{name}, EMAIL=#{email}, PHONE=#{phone} WHERE STUD_ID=#{studId} </update>
可以如下调用这个sql语句:
int updateCount = sqlSession.update("com.briup.mappers.StudentMapper.updateStudent", student);
sqlSession.update()方法返回执行UPDATE语句之后影响的行数。
或者使用映射接口Mapper来调用:
public interface StudentMapper{ int updateStudent(Student student); } StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); int updateCount = mapper.updateStudent(student);
一个DELETE语句可以在<delete>标签元素中进行配置,如下所示:
<delete id="deleteStudent" parameterType="int"> DELETE FROM STUDENTS WHERE STUD_ID=#{id} </delete>
可以如下调用这个sql语句:
int deleteCount = sqlSession.delete("com.briup.mappers.StudentMapper.deleteStudent", 1);
sqlSession.delete()方法返回 delete 语句执行后影响的行数。
或者使用映射接口Mapper来调用:
public interface StudentMapper{ int deleteStudent(int studId); } StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); int deleteCount = mapper.deleteStudent(studId);
MyBatis真正强大的功能,在于映射SELECT查询结果到java的各种类型上。
<select id="findStudentById" parameterType="int" resultType="Student"> SELECT STUD_ID, NAME, EMAIL, PHONE FROM STUDENTS WHERE STUD_ID=#{stud Id} </select>
可以如下调用这个sql语句:
Student student = sqlSession.selectOne("com.briup.mappers. StudentMapper.findStudentById", 1);
或者使用映射接口Mapper来调用:
public interface StudentMapper{ Student findStudentById(Integer studId); } StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); Student student = mapper.findStudentById(studId);
注意:这时候 Student
对象的 studId
属性值为空,但是其他属性的值都有。
因为数据库表中的字段为 stud_id
,类中属性名为 studId
,所以没有自动把值封装进来。
这时给 stud_id
列起一个别名为 studId
,和类中属性名保持一致即可。如下所示:
<select id="findStudentById" parameterType="int" resultType="Student"> SELECT STUD_ID AS studId, NAME,EMAIL, PHONE FROM STUDENTS WHERE STUD_ID=#{studId} </select>
<select id="findAllStudents" resultType="Student"> SELECT STUD_ID AS studId, NAME,EMAIL, PHONE FROM STUDENTS </select>
可以如下调用这个sql语句:
List<Student> students = sqlSession.selectList("com.briup.mappers.StudentMapper.findAllStudents");
或者使用映射接口Mapper来调用:
public interface StudentMapper{ List<Student> findAllStudents(); } StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); List<Student> students = mapper.findAllStudents();
注意,除了List集合类型,也可以使用其他类型的集合类,如Set、Map等。MyBatis会根据集合的类型,采用适当的集合实现,
如下所示:
java.util.ArrayList java.util.HashMap java.util.HashSet java.util.TreeSet
resultMap被用来将 SELECT 语句的结果集映射到java对象的属性中。
在映射文件中,可以先定义出结果集映射resultMap,然后在一些SELECT语句上引用这个 resultMap。
MyBatis的结果集映射resultMap非常强大,可以使用它指定sql查询出的结果集,会被怎么处理并封装成对象,也可以使用它完成复杂查询的映射,例如一对一、一对多关系的SELECT语句。
定义一个resultMap,代码如下:
<resultMap id="StudentResult" type="com.briup.pojo.Student"> <id property="studId" column="stud_id" /> <result property="name" column="name" /> <result property="email" column="email" /> <result property="phone" column="phone" /> </resultMap> <select id="findAllStudents" resultMap="StudentResult"> SELECT * FROM STUDENTS </select> <select id="findStudentById" parameterType="int" resultMap="StudentResult"> SELECT * FROM STUDENTS WHERE STUD_ID=#{studId} </select>
resultMap标签中的属性:
resultMap标签中的子标签:
<result>
子标签:用来将查询的数据中,一个指定 resultset
列类型的类 映射到对象的一个属性中。 <id>
子标签:和 <result>
标签功能相同,但是 <id>
用来映射的是表中的主键。 注意1:在<select>标签中,使用的是resultMap属性,而不是resultType属性。当<select>标签中配置了resutlMap属性,MyBatis会根据resutlMap标签中定义的列名与对象属性名的【对应关系】来自动填充对象中的属性值。
注意2:resultType和resultMap二者只能用其一,不能同时使用。resultType属性指的是结果集将自动封装成什么类型。这时候默认表中列的名字和类中属性名字一致,不一致的话可以在查找的时候使用取别名的方式使其一致。
resultMap 属性指的是结果集将按照<resultMap>标签中定义的 【对应关系】 来封装数据。如果使用resultMap,在查找的时候如果有数据库中的列名和对象属性不一致的情况也不要紧,可以直接查找。常用resultMap这种形式
<select id="findStudentById" parameterType="int" resultType="map"> SELECT * FROM STUDENTS WHERE STUD_ID=#{studId} </select>
在上述的<select>语句中,将resultType配置成map,这时候,结果集的【列名】将会作为Map中的key值,而【列值】将作为Map的value值。
HashMap<String,Object> studentMap = sqlSession.selectOne("com.briup.mappers.StudentMapper.findStudentById", studId); studentMap.foreach((k,v)->System.out.println(k+" = "+v));
<select id="findAllStudents" resultType="map"> SELECT STUD_ID, NAME, EMAIL, PHONE FROM STUDENTS </select>
由于 resultType="map"
,以及结果集为多行,最终返回的数据类型默认是 List<Map<String,Object>>
,
List<Map<String, Object>> studentMapList = sqlSession.selectList("com.briup.mappers.StudentMapper.findAllStudents");
如下所示:
List<Map<String, Object>> studentMapList = sqlSession.selectList("com.briup.mappers.StudentMapper.findAllStudents"); for(Map<String, Object> studentMap : studentMapList) { studentMap.foreach((k,v)->System.out.println(k+" = "+v)); System.out.println("----------------"); }
<select id="findAllStudents_student" resultType="Student"> SELECT STUD_ID AS STUDID,NAME,EMAIL,DOB FROM STUDENTS </select>
在该映射文件对应的映射接口中:
方法的返回类型可以随便定为常见的集合类型,Mybatis会按照我们方法中声明的集合类型来进行自动封装。
但是要注意使用SortedSet的时候,Student类需要实现Comparable接口,否则是不能进行排序的
例如:
public List<Student> findAllStudents_List(); 或者 public Set<Student> findAllStudents_Set(); 或者 public SortedSet<Student> findAllStudents_SortedSet();
<select id="findAllName_list" resultType="String"> SELECT NAME FROM STUDENTS </select>
在该映射文件对应的映射接口中: 把查询到所有名字都放到List集合中并返回
public List<String> findAllName_list();
<select id="findCount_int" resultType="int"> SELECT count(*) FROM STUDENTS </select>
在该映射文件对应的映射接口中,把查询到的这个值直接返回
public int findCount_int();
(注:这个可以在下面的一对一映射中进行测试,因为这里需要建立一对一关系的表结构)
在sql映射文件中,可以让一个<resultMap>其继承(配置重写)另一个<resultMap>,这样,第一个<resultMap>中定义的对应关系,就可以被第二个<resultMap>继承过来,例如:
<!-- 第一个resultMap --> <resultMap type="Student" id="StudentResult"> <id property="studId" column="stud_id" /> <result property="name" column="name" /> <result property="email" column="email" /> <result property="phone" column="phone" /> </resultMap>
假设新增了一个新的实体类Address,该类中有多个属性,分别和数据库中ADDRESSES表对应。
在Student类中又新增加了一个属性Address。
<!-- 第二个resultMap --> <resultMap type="Student" id="StudentWithAddressResult" extends="StudentResult"> <result property="address.addrId" column="addr_id" /> <result property="address.street" column="street" /> <result property="address.city" column="city" /> <result property="address.state" column="state" /> <result property="address.zip" column="zip" /> <result property="address.country" column="country" /> </resultMap>
其中id为 StudentWithAddressResult 的 resultMap,拓展了id为 StudentResult 的 resultMap
如果只想映射Student数据,你可以使用id为StudentResult的resultMap,如下所示:
<select id="findStudentById" parameterType="int" resultMap="StudentResult"> SELECT * FROM STUDENTS WHERE STUD_ID=#{stud Id} </select>
如果你想将映射Student数据和Address数据,你可以使用id为StudentWithAddressResult的 resultMap:
<select id="selectStudentWithAddress" parameterType="int" resultMap="StudentWithAddressResult"> SELECT STUD_ID, NAME, EMAIL, PHONE, A.ADDR_ID, STREET, CITY, STATE, ZIP, COUNTRY FROM STUDENTS S LEFT OUTER JOIN ADDRESSES A ON S.ADDR_ID=A.ADDR_ID WHERE STUD_ID=#{studId} </select>
注:该sql语句使用了连接查询中的外连接,也可以使用等值连接