mybatis的sql映射文件

映射器配置文件

映射配置文件主要完成的是对数据库的操作,我们直接将sql语句写在映射配置文件中,比如下面的例子:

<?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.mybatis3.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>

上面的文件就是一个映射配置文件:

  • namespace是命名空间
  • id是该标签的唯一标识符,在同一个命名空间中该id唯一
  • parameterType是输入参数的类型
  • resultType是输出参数的类型

在java代码中我们想要调用该查询语句可以使用下面的代码:

public Student findStudentById(Integer studId){
    //通过封装类来获取sqlSession
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    try{
        Student student = sqlSession.selectOne("com.mybatis3.mappers.StudentMapper.
        findStudentById", studId);
        return student;
    }finally{
    	sqlSession.close();
    }
}

然后再通过sqlSession的各种api(这里使用的是selectOne())去调用我们的sql映射。对于这种方式只有当我们执行的时候才会知道是否出错,所以还可以使用另外一种更好的方式。

mapper映射器接口

另一种方式是当配置映射文件后,可以创建一个完全对应的映射器接口,所谓的完全对应主要有以下几个方面:

  • 接口所在的包和配置文件所在的包一致
  • 接口名与配置文件名一致
    上面两条结合起来比如StudentMapper.xml所在的包名是com.mybatis.mappers那么对应的接口名就是com.mybatis.mappers.StudentMapper.java
  • 接口中的方法名就是配置文件中id值
  • 方法参数类型为parameterType对应值
  • 方法返回值类型为returnType对应值
  • 命名空间namespace与接口的完全限定名一致

对应上述的StudentMapper.xml文件,可以创建一个映射器接口StudentMapper.java

package com.mybatis.mapper;
public interface StudentMapper{
    Student findStudentById(Integer id);
}

使用接口映射器

public Student findStudentById(Integer stuId){
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    try{
        StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
        return studentMapper.findStudentById(stuId);
    }finally{
        sqlSession.close();
    }
}

映射语句

Mybatis提供了多种元素来配置不同类型的语句,比如selectinsertupdatedelete,不过需要注意的是这些元素只是为了增强可读性,并不是说select语句必须写在select元素里面。但是为了良好的可读性还是要将对应类型的sql语句写在对应的标签里面比较好。

insert语句

insert配置

<insert id="insertStudent" parameterType="Student">
    INSERT INTO STUDENTS(STUD_ID,NAME,EMAIL, PHONE)
    VALUES(#{studId},#{name},#{email},#{phone})
</insert>
  • id是在当前命名空间中唯一的值
  • parameterType是输入参数类型

insert调用

int count = sqlSession.insert("com.mybatis3.mappers.StudentMapper.insertStudent", student);

该方法返回的值是影响的行数。

insert语句自动生成主键配置

mysql

mysql中可以为自动生成(auto-generated)主键的咧stud_id插入值,可以使用useGenerateKeyskeyProperty属性让数据库生成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属性上。

StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
mapper.insertStudent(student);
int studId = student.getStudId();
oracle
order=“before”

oracle是不支持auto_increment列,而是使用序列(sequence)来生成主键值,假如我们有一个stud_id_seq的序列来生成stud_id主键值,那么可以使用如下代码来生成主键:

<insert id="insertStudent" parameterType="Student">
    <selectKey keyProperty="studId" resultType="int" order="BEFORE">
    SELECT STUD_ID_SEQ.NEXTVAL FROM DUAL
    </selectKey>
    INSERT INTO STUDENTS(STUD_ID,NAME,EMAIL, PHONE)
    VALUES(#{studId},#{name},#{email},#{phone})
</insert>

使用子元素类生成主键值,并将值保存到Student对象的studId属性上,上面的order=“before”表示mybatis将取得序列的下一个值作为主键值,并且在执行insert sql语句之前将值设置到studId属性上。

order=“after”

上面使用的是order=“before”,order的值还可以为after,获取序列的下一个值时使用触发器(trigger)来设置主键值,并且在执行insert sql语句之前将值设置到主键列上,此时的配置文件为:

<insert id="insertStudent" parameterType="Student">
    INSERT INTO STUDENTS(NAME,EMAIL, PHONE)
    VALUES(#{name},#{email},#{phone})
    <selectKey keyProperty="studId" resultType="int" order="AFTER">
    SELECT STUD_ID_SEQ.CURRVAL FROM DUAL
    </selectKey>
</insert>

updatedelete语句是类似的,所以不再写,重点是select语句。

select语句

一个简单的查询的映射

<select id="findStudentById" parameterType="int" resultType="Student">
    SELECT STUD_ID, NAME, EMAIL, PHONE FROM STUDENTS WHERE STUD_ID=#{studId}
</select>
命名空间方式调用
int studId =1;
Student student = sqlSession.selectOne("com.mybatis3.mappers.
StudentMapper.findStudentById", studId);
映射接口调用
package com.mybatis3.mappers;
public interface StudentMapper{
	Student findStudentById(Integer studId);
}
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student student = mapper.findStudentById(studId);

查询映射需要注意的问题(属性与字段对应规则)

在上面的例子查询结果中student对象的studId属性是不会有值的,这是因为mybatis自动对javaBean中的列中和列名匹配的属性进行填充,表中是stud_id,javaBean中是studId,二者是不对应的,如果对应可以将查询结果取别名:

<select id="findStudentById" parameterType="int" resultType="Student">
    SELECT STUD_ID AS studId, NAME,EMAIL, PHONE FROM STUDENTS WHERE STUD_ID=#{studId}
</select>

select返回多条结果

返回多条结果时配置文件与返回一条是一样的,resultType返回的依然是对象而不是一个集合。

<select id="findAllStudents" resultType="Student">
    SELECT STUD_ID AS studId, NAME,EMAIL, PHONE FROM STUDENTS
</select>
通过命名空间调用
List<Student> students =
sqlSession.selectList("com.mybatis3.mappers.StudentMapper.findAllStudents");
通过接口映射器调用

定义接口

package com.mybatis3.mappers;
public interface StudentMapper{
	List<Student> findAllStudents();
}

调用

StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> students = mapper.findAllStudents();

结果集映射ResultMaps

ResultMaps是用来将查询结果集映射到javaBean的属性中。使用步骤:

  • 定义一个车ResultMap结果集
  • select标签中引用resultMap

一个简单的案例:

<resultMap id="StudentResult" type="com.mybatis3.domain.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的id在同一个命名空间内部应该是唯一的
  • type属性是完全限定类名或者是返回类型的别名
  • resultTyperesultMap两者之间只能使用一个,不能同时使用

将结果集映射到HashMap中

如果将resultType类型设置为map(java.util.HashMap),结果集的列名将会作为Map的key值,列值作为Map的value值。

返回单个结果

配置文件:

<select id="findStudentById" parameterType="int" resultType="map">
	SELECT * FROM STUDENTS WHERE STUD_ID=#{studId}
</select>

部分测试代码:

HashMap<String,Object> studentMap = sqlSession.selectOne("com.
mybatis3.mappers.StudentMapper.findStudentById", studId);
System.out.println("stud_id :"+studentMap.get("stud_id"));
System.out.println("name :"+studentMap.get("name"));
System.out.println("email :"+studentMap.get("email"));
System.out.println("phone :"+studentMap.get("phone"));

返回多个结果

配置文件:

<select id="findAllStudents" resultType="map">
	SELECT STUD_ID, NAME, EMAIL, PHONE FROM STUDENTS
</select>

部分测试代码:

List<HashMap<String, Object>> studentMapList =
sqlSession.selectList("com.mybatis3.mappers.StudentMapper.findAllS
tudents");
for(HashMap<String, Object> studentMap : studentMapList){
    System.out.println("studId :" + studentMap.get("stud_id"));
    System.out.println("name :" + studentMap.get("name"));
    System.out.println("email :" + studentMap.get("email"));
    System.out.println("phone :" + studentMap.get("phone"));
}

拓展ResultMap

这里的拓展类似于java中的继承。

配置文件案例:

<!--这里type直接写student是因为有了别名-->
<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>
<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为StudentWithAddressResultResultMap继承了idStudentResultResultMap

这样如果只想映射Student的话那么可以只使用idStudentResultResultMap就可以,但是如果想要映射StudentAddress数据,那么就可以使用id为StudentWithAddressResultResultMap

一对一映射

基本一对一映射案例

比如现在有两个表StudentAddress关系如下:

Student表:

stud_idnameemailphoneaddr_id
1张三hrgqg@qq.com124131
2李四a1232@qq.com35452

Address表:

addr_idstreefcitystatezipcountry
1jafgoiHK78391398usa
2hairogHK819389131usa

这两个表是有关联的,Student表与Address表之间的关联是Student表中的addr_id是外键。

此时的javaBean代码如下:

AddressBean:

public class Address{
    private Integer addrId;
    private String street;
    private String city;
    private String state;
    private String zip;
    private String country;
    // setters & getters
}

StudentBean:

public class Student{
    private Integer studId;
    private String name;
    private String email;
    private PhoneNumber phone;
    private Address address;
    //setters & getters
}

配置文件:

<resultMap type="Student" id="StudentWithAddressResult">
    <id property="studId" column="stud_id" />
    <result property="name" column="name" />
    <result property="email" column="email" />
    <result property="phone" column="phone" />
    <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>
<select id="selectStudentWithAddress" parameterType="int"
resultMap="StudentWithAddressResult">
    SELECT STUD_ID, NAME, EMAIL, 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>

由于在Student对象中包含Address对象,所以在配置文件中可以使用圆点记法为内嵌对象赋值。

上面这种方式如果想要查询Address就相当于也要查询Student,这样会有重复映射,此时可以使用嵌套结果ResultMap和嵌套select查询语句。

嵌套ResultMap来实现一对一映射

has-one关联

依然是上面的案例,此时的配置文件如下:

<resultMap type="Address" id="AddressResult">
    <id property="addrId" column="addr_id" />
    <result property="street" column="street" />
    <result property="city" column="city" />
    <result property="state" column="state" />
    <result property="zip" column="zip" />
    <result property="country" column="country" />
</resultMap>
<resultMap type="Student" id="StudentWithAddressResult">
    <id property="studId" column="stud_id" />
    <result property="name" column="name" />
    <result property="email" column="email" />
    <association property="address" resultMap="AddressResult" />
</resultMap>
<select id="findStudentWithAddress" parameterType="int"
resultMap="StudentWithAddressResult">
    SELECT STUD_ID, NAME, EMAIL, 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>

元素被用来导入“has-one”类型的关联,在上面的例子中使用元素引用了在同一个xml文件中定义的(resultMap的值是另外一个ResultMapid)

内联关联

内联关联配置文件如下:

<resultMap type="Student" id="StudentWithAddressResult">
    <id property="studId" column="stud_id" />
    <result property="name" column="name" />
    <result property="email" column="email" />
    <association property="address" javaType="Address">
        <id property="addrId" column="addr_id" />
        <result property="street" column="street" />
        <result property="city" column="city" />
        <result property="state" column="state" />
        <result property="zip" column="zip" />
        <result property="country" column="country" />
    </association>
</resultMap>

使用嵌套Select来实现一对一映射

配置文件:

<resultMap type="Address" id="AddressResult">
    <id property="addrId" column="addr_id" />
    <result property="street" column="street" />
    <result property="city" column="city" />
    <result property="state" column="state" />
    <result property="zip" column="zip" />
    <result property="country" column="country" />
</resultMap>
<select id="findAddressById" parameterType="int"
resultMap="AddressResult">
	SELECT * FROM ADDRESSES WHERE ADDR_ID=#{id}
</select>
<resultMap type="Student" id="StudentWithAddressResult">
	<id property="studId" column="stud_id" />
    <result property="name" column="name" />
	<result property="email" column="email" />
	<association property="address" column="addr_id" 						select="findAddressById" />
</resultMap>
<select id="findStudentWithAddress" parameterType="int"
resultMap="StudentWithAddressResult">
	SELECT * FROM STUDENTS WHERE STUD_ID=#{Id}
</select>

在此方式中,元素的 select 属性被设置成了 idfindAddressById 的语句。这里,两个分开的SQL 语句将会在数据库中执行,第一个调用 findStudentById 加载 student 信息,而第二个调用 findAddressById 来加载 address 信息。Addr_id 列的值将会被作为输入参数传递给 selectAddressById 语句。

调用语句:

StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student student = mapper.findStudentWithAddress(studId);
System.out.println(student);
System.out.println(student.getAddress());

一对多映射

案例表数据

tutors:

tutor_idtutor_nameemailphoneaddr_id
1张三ad@qq.com32131
2李四asg@qq.com15252

course:

course_idnamedescriptionstart_dateend_datetutor_id
1javaSEjava Se2019-01-012019-01-011
2javaEEjava Ee2019-01-012019-01-012
3MybatisMybatis2019-01-012019-01-012

上面教授与课程表是一对多(外键在多的一方)的关联,可以看到李四对应两门课程。

tutorBean

public class Tutor{
    private Integer tutorId;
    private String name;
    private String email;
    private Address address;
    private List<Course> courses;
    // setters & getters
}

CourseBean

public class Course{
    private Integer courseId;
    private String name;
    private String description;
    private Date startDate;
    private Date endDate;
    private Integer tutorId;
	//setters & getters
}

使用内嵌结果ResultMap实现一对多

<resultMap type="Course" id="CourseResult">
    <id column="course_id" property="courseId" />
    <result column="name" property="name" />
    <result column="description" property="description" />
    <result column="start_date" property="startDate" />
    <result column="end_date" property="endDate" />
</resultMap>
<resultMap type="Tutor" id="TutorResult">
	<id column="tutor_id" property="tutorId" />
    <result column="tutor_name" property="name" />
	<result column="email" property="email" />
	<collection property="courses" resultMap="CourseResult" />
</resultMap>
<select id="findTutorById" parameterType="int" resultMap="TutorResult">
    SELECT T.TUTOR_ID as tutorId , T.tutor_NAME AS NAME, EMAIL, 
    C.COURSE_ID as courseId,   C.NAME, DESCRIPTION, START_DATE as
    StartDate,   END_DATE as endDate
    FROM TUTORS T LEFT OUTER JOIN ADDRESSES A ON T.ADDR_ID=A.ADDR_ID
    LEFT OUTER JOIN COURSES C ON T.TUTOR_ID=C.TUTOR_ID
    WHERE T.TUTOR_ID=#{tutorId}
</select>

使用嵌套Select语句实现一对多映射

<resultMap type="Course" id="CourseResult">
    <id column="course_id" property="courseId" />
    <result column="name" property="name" />
    <result column="description" property="description" />
    <result column="start_date" property="startDate" />
    <result column="end_date" property="endDate" />
</resultMap>
<resultMap type="Tutor" id="TutorResult">
    <id column="tutor_id" property="tutorId" />
    <result column="tutor_name" property="name" />
    <result column="email" property="email" />
    <association property="address" resultMap="AddressResult" />
    <collection property="courses" column="tutor_id" select="findCoursesByTutor" />
</resultMap>
<select id="findTutorById" parameterType="int" resultMap="TutorResult">
    SELECT T.TUTOR_ID tutorId, T.TUTOR_NAME AS NAME, EMAIL
    FROM TUTORS T WHERE T.TUTOR_ID=#{tutorId}
</select>
<select id="findCoursesByTutor" parameterType="int" 
    resultMap="CourseResult">
    SELECT * FROM COURSES WHERE TUTOR_ID=#{tutorId}
</select>

注:嵌套 Select 语句查询会导致 N+1 选择问题。首先,主查询将会执行(1 次),对于主
查询返回的每一行,另外一个查询将会被执行(主查询 N 行,则此查询 N 次)。对于
大型数据库而言,这会导致很差的性能问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值