1. Mybatis的映射配置文件Mapper.xml



1.1 输入参数映射

    1. 在Mapper映射文件中,可以通过parameterType指定SQL语句所要输入参数的类型,类型可以是java简单类型(String和七个基本类型以及基本类型的包装类)、hashmap、pojo的包装类型。

<?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="dao.UserMapper">
	<select id="findUserById" parameterType="java.lang.String" resultType="entity.User">
		select * from cn_user where cn_user_id=#{id}
	</select>
</mapper>

    2. 在查询或其他SQL语句中,所需要的参数可能是多个,而parameterType属性只能指定一个输入参数的类型,也就是只能接受一个对象,所以我们需要将这些参数封装到一个包装类中,这个包装类必须符合JavaBean规范,有时候需要传入查询条件很复杂,可能包括用户信息、其它信息等,而每一个数据库表都对应这一个Java实体类,除非数据库表的字段发生变化,一般不修改原实体类,建议使用自定义的包装类型的pojo,在包装类型的pojo中将复杂的查询条件包装进去,比如

//与数据库User表对应的实体类,类中的每个属性名与user表的字段一致,而且符合javabean规范
public class User implements Serializable{
	private String cn_user_id;
	private String cn_user_name;
	private String cn_user_password;
	public String getCn_user_id() {
		return cn_user_id;
	}
	public void setCn_user_id(String cn_user_id) {
		this.cn_user_id = cn_user_id;
	}
	public String getCn_user_name() {
		return cn_user_name;
	}
	public void setCn_user_name(String cn_user_name) {
		this.cn_user_name = cn_user_name;
	}
	public String getCn_user_password() {
		return cn_user_password;
	}
	public void setCn_user_password(String cn_user_password) {
		this.cn_user_password = cn_user_password;
	}
	@Override
	public String toString() {
		return "User [cn_user_id=" + cn_user_id + ", cn_user_name=" + cn_user_name + "]";
	}
	
}
/**
 * User应用扩展类,用于应变多种需求而创造的类
 * 通常不在源类Uesr上直接进行修改,而是通过继承来添加一些额外的属性,该类也必须符合javabean规范
 *
 */
public class UserCustom extends User{
	//添加某些属性
}
//通过该类所需要的几个参数所在的扩展类的对象作为属性组合进来,参数输入映射就通过该类完成
public class queryUserVo{
	private UserCustom user;
    //或包含其他属性
	public User getUser() {
		return user;
	}
	public void setUser(UserCustom user) {
		this.user = user;
	}
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return user.getCn_user_name();
	}
}
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="dao.UserDao">
<!--通过 属性.属性.属性··· 的表达式从传入的参数包装对象中取出所需要的参数数据-->
	<select id="findUserById" parameterType="queryUserVo" resultType="UserCustom">
		select * from cn_user where cn_user_id=#{user.cn_user_id}
	</select>

</mapper>



1.2 输出参数映射



    1. resultType
  1. 在Mapper映射文件中,可以通过resultType指定SQL语句查询结果所要映射的输出类型,类型可以是java简单类型(String和七个基本类型以及基本类型的包装类)、hashmap、pojo的包装类型。使用resultType进行输出映射,只有查询出来的列名(可能会在SQL语句中给查询的字段起别名)和pojo中的属性名一致,该列才可以映射成功。如果查询出来的列名和pojo中的属性名全部不一致,没有创建pojo对象。只要查询出来的列名和pojo中的属性有一个一致,就会创建pojo对象,不一致的属性的值为null。
  2. 查询出来的结果集必须只有一行且一列,可以使用简单类型(String和7个基本类型)进行输出映射。
  3. 不管是输出的pojo单个对象还是一个列表(list中包括pojo),在mapper.xml中resultType指定的类型是一样的。具体区别在mapper.java指定的方法返回值类型不一样
  • 输出单个pojo对象,方法返回值是单个对象类型

java mapper xml格式 mapper.java文件_User

  • 输出pojo对象list,方法返回值是List<Pojo>

java mapper xml格式 mapper.java文件_User_02

    2. resultMap

    1. 在Mapper映射文件中,可以通过resultMap进行高级输出映射。

    2. 如果查询出来的列名和pojo的属性名不一致,通过定义一个resultMap对列名和pojo属性名之间作一个映射关系。首先要定义resultMap,然后使用resultMap作为statement的输出映射类型。

<!--比如下面的SQL语句中查询得到的字段名为name而不是cn_user_name,与User中的属性名无法对应,会产生问题-->
	<select id="findUserById" parameterType="java.lang.String" resultType="entity.User">
		select cn_user_name name from cn_user where cn_user_id=#{id}
	</select>
<!--使用resultMap-->
	<select id="findUserById" parameterType="java.lang.String" resultMap="username">
		select cn_user_name name from cn_user where cn_user_id=#{id}
	</select>
	<!-- type指查询结果集所映射的Java类型,可以是别名或全限定名
		id指该resultMap的唯一标识 -->
	<resultMap type="entity.User" id="username">
		<!-- result标签指定java类型中的属性property与查询到的字段column之间的映射关系 -->
		<result column="name" property="cn_user_name"/>
	</resultMap>

 

    3. 关联查询,首先搞清一对一、一对多和多对多查询,User表、NoteBook表和Note表,NoteBook表以User表的主键作为外键进行关联,Note表以NoteBook表的主键作为外键进行关联:

  • 一对多:比如User用户表与NoteBook笔记本表之间,查询一个用户的所有笔记本,一个用户可以有多个笔记本,User为查询主表,NoteBook为关联表,也就是通过User表中的主键与NoteBook表的外键的关联关系来查询NoteBook表中的数据,所以就是一对多的关系
  • 一对一:比如User用户表与NoteBook笔记本表之间,查询笔记本的用户,一个笔记本只能有一个用户,NoteBook为查询主表,User表为关联表,也就是通过NoteBook表中的外键与User表主键的关联关系来查询,所以就是一对一的关系;                                                                                            一对一关联查询可以使用resultType,使用resultType实现较为简单,如果pojo中没有包括查询出来的列名,需要增加列名对应的属性,即可完成映射,如果没有查询结果的特殊要求建议使用resultType。  
    如果使用resultMap实现一对一关联查询,需要单独定义resultMap,实现有点麻烦,如果对查询结果有特殊的要求,使用resultMap可以完成将关联查询映射pojo的属性中。
     
    resultMap可以实现延迟加载,resultType无法实现延迟加载。
  • 多对多:比如User用户表与NoteBook笔记本表与Note笔记表之间,查询一个用户的所有笔记本以及笔记,一个用户会有多个笔记本,一个笔记本会包含多个笔记,User表与Note表之间无直接关联关系,而是通过NoteBook表建立间接关联,User为查询主表,NoteBook笔记本表与Note笔记表为关联表,通过User表中的主键与NoteBook表的外键的关联关系来查询用户有多少笔记本,再通过每一个笔记本的主键与笔记中外键的关联关系查询到每一个笔记本的所有笔记,这就是多对多查询

    4. resultMap对于一对一查询的使用:以User用户表与NoteBook笔记本表为例,NoteBook表为查询主表,通过NoteBook表中的User表外键关联查询User表中的数据

 

  • 首先写好pojo包装映射类,因为NoteBook表为查询主表,所以pojo类继承NoteBook实体类,然后再添加所要关联查询的User属性,甚至是可以将整个User类作为pojo类的属性,一个NoteBook对象对应一个User对象
public class NoteBook implements Serializable {
	private String cn_notebook_id;
	private String cn_user_id;
	private String cn_notebook_type_id;
	private String cn_notebook_name;
	private String cn_notebook_desc;
	private Timestamp cn_notebook_createtime;
//省略get/set方法
	
}
public class User implements Serializable{
	private String cn_user_id;
	private String cn_user_name;
	private String cn_user_password;
	private String cn_user_token;
	private String cn_user_nick;
//省略get/set方法
}
//如果只需要查询得到User中的部分属性,可以直接写属性,而不需要将整个User对象作为属性
public class UserAndNoteBook1 extends NoteBook {
	
	private String cn_user_name;
	//  private String cn_user_id;
	public String getCn_user_name() {
		return cn_user_name;
	}
	public void setCn_user_name(String cn_user_name) {
		this.cn_user_name = cn_user_name;
	}
	
}
public class UserAndNoteBook2 extends NoteBook{
	private User user;
	public User getUser() {
		return user;
	}

	public void setUser(User user) {
		this.user = user;
	}
}
  • 编写Mapper映射文件中的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="dao.NoteBookDao">
<!-- 通过resultType实现一对一映射
	将整个查询的结果映射到entity.UserAndNoteBook1类中
	 -->
	<select id="findNoteBookAndUser1" parameterType="String" resultType="entity.UserAndNoteBook1">
		select cn_notebook.*,cn_user_name from cn_notebook,cn_user 
		where cn_user.cn_user_id=cn_notebook.cn_user_id and cn_notebook.cn_notebook_id=#{id}
	</select>

<!-- 通过resultMap实现一对一映射
	 -->
	<select id="findNoteBookAndUser2" parameterType="String" resultMap="UserAndNoteBook">
		select cn_notebook.*,cn_user.* from cn_notebook,cn_user 
		where cn_user.cn_user_id=cn_notebook.cn_user_id and cn_notebook.cn_notebook_id=#{id}
	</select>

<!-- 定义映射结果集resultMap
	type指定将整个查询的结果映射到entity.UserAndNoteBook2类中,
id为当前定义的resultMap在整个Mapper文件中唯一标示,通过该id属性来引用该resultMap
	 -->
	<resultMap type="entity.UserAndNoteBook2" id="UserAndNoteBook">
<!-- id:指定查询列中的主键,订单信息的中的主键,如果有多个主键,配置多个id
			column:数据库表中的字段名
			property:类中的属性名
通过column和property将数据库表中的字段映射到pojo类中指定的属性中
		  -->
		<id column="cn_notebook_id" property="cn_notebook_id"/>
		<result column="cn_user_id" property="cn_user_id"/>
		<result column="cn_notebook_type_id" property="cn_notebook_type_id"/>
		<result column="cn_notebook_name" property="cn_notebook_name"/>
		<result column="cn_notebook_desc" property="cn_notebook_desc"/>
		<result column="cn_notebook_createtime" property="cn_notebook_createtime"/>
<!-- association:用于映射关联查询单个对象的信息,一对一关联映射查询实现的关键标签
		property:要将关联查询的用户信息映射到UserAndNoteBook2类中的user属性中
        javaType:指定映射的user属性的类型
		 -->
		<association property="user" javaType="entity.User">
			<id column="cn_user_id" property="cn_user_id"/>
			<result column="cn_user_name" property="cn_user_name"/>
			<result column="cn_user_password" property="cn_user_password"/>
			<result column="cn_user_token" property="cn_user_token"/>
			<result column="cn_user_nick" property="cn_user_nick"/>
		</association>
	</resultMap>
</mapper>

    5. resultMap对于一对多查询的使用:以User用户表与NoteBook笔记本表为例,User表为查询主表,通过User表中的主键关联查询NoteBook表中的数据,一行User数据(或者说一个User对象)对应多行NoteBook数据(多个NoteBook对象)

  • 首先写好pojo保证类,以User表为主表,所以应继承User类,一个User对象对应多个NoteBook对象,所以pojo类中添加一个List<NoteBook>类型的books属性
public class UserCustom extends User{
	private List<NoteBook> books;

	public List<NoteBook> getBooks() {
		return books;
	}

	public void setBooks(List<NoteBook> books) {
		this.books = books;
	}
}
  • 编写Mapper映射文件中的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="dao.UserDao">

	<select id="findUserWithNoteBook" parameterType="java.lang.String" resultMap="UserWithNoteBook">
		SELECT
		cn_notebook.*, cn_user.*
		FROM
		cn_notebook,
		cn_user
		WHERE
		cn_user.cn_user_id = cn_notebook.cn_user_id and cn_user.cn_user_id=#{id}
	</select>
	<resultMap id="UserWithNoteBook" type="entity.UserCustom">
		<id column="cn_user_id" property="cn_user_id"/>
		<result column="cn_user_name" property="cn_user_name"/>
		<result column="cn_user_password" property="cn_user_password"/>
		<result column="cn_user_token" property="cn_user_token"/>
<!--对于一对多关系映射,应使用collection 子标签,
property指定将所有查询到的NoteBook集合存放到books属性对象中,
ofType指定查询到的每一条NoteBook数据都映射为entity.NoteBook类对象
-->
		<collection property="books" ofType="entity.NoteBook">
			<id column="cn_notebook_id" property="cn_notebook_id"/>
			<result column="cn_user_id" property="cn_user_id"/>
			<result column="cn_notebook_type_id" property="cn_notebook_type_id"/>
			<result column="cn_notebook_name" property="cn_notebook_name"/>
			<result column="cn_notebook_desc" property="cn_notebook_desc"/>
			<result column="cn_notebook_createtime" property="cn_notebook_createtime"/>
		</collection>
	</resultMap>

</mapper>

    6. resultMap对于多对多查询的使用:以User用户表与NoteBook笔记本表与Note笔记表为例,User表为查询主表,通过User表中的主键关联查询NoteBook表中的数据,一行User数据(或者说一个User对象)对应多行NoteBook数据(多个NoteBook对象),然后再通过查询到的每一个NoteBook中的主键id,关联查询该NoteBook有多少Note

  • 首先定义pojo包装类:User为主表,所以pojo继承User类;要通过NoteBook关联查询得到Note,所以还得对NoteBook创建一个扩展pojo类,该pojo类要添加一个List<Note>类型的notes属性;User类的扩展pojo类就要添加一个以NoteBook的扩展pojo类作为泛型的集合属性books
public class Note implements Serializable{
	private String cn_note_id;
	private String cn_notebook_id;
	private String cn_note_title;
	private String cn_note_body;
//省略get/set方法
}

public class NoteBookVo extends NoteBook{
	private List<Note> notes;
	public List<Note> getNotes() {
		return notes;
	}
	public void setNotes(List<Note> notes) {
		this.notes = notes;
	}
}
public class UserVo extends User{
	private List<NoteBookVo> books;

	public List<NoteBookVo> getBooks() {
		return books;
	}

	public void setBooks(List<NoteBookVo> books) {
		this.books = books;
	}
	
}
  • 编写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="dao.UserDao">
	<select id="find" parameterType="java.lang.String" resultMap="UserVo">
		SELECT
		cn_notebook.*, cn_user.*,cn_note.*
		FROM
		cn_notebook,
		cn_user,
		cn_note
		WHERE
		cn_user.cn_user_id = cn_notebook.cn_user_id and cn_user.cn_user_id=#{id} and cn_note.cn_notebook_id=cn_notebook.cn_notebook_id
	</select>
		<resultMap id="UserVo" type="entity.UserVo">
		<id column="cn_user_id" property="cn_user_id"/>
		<result column="cn_user_name" property="cn_user_name"/>
		<result column="cn_user_password" property="cn_user_password"/>
		<result column="cn_user_token" property="cn_user_token"/>
		
		<collection property="books" ofType="entity.NoteBookVo">
			<id column="cn_notebook_id" property="cn_notebook_id"/>
			<result column="cn_user_id" property="cn_user_id"/>
			<result column="cn_notebook_type_id" property="cn_notebook_type_id"/>
			<result column="cn_notebook_name" property="cn_notebook_name"/>
			<result column="cn_notebook_desc" property="cn_notebook_desc"/>
			<result column="cn_notebook_createtime" property="cn_notebook_createtime"/>
			
			<collection property="notes" ofType="entity.Note">
				<id column="cn_note_id" property="cn_note_id"/>
				<result column="cn_notebook_id" property="cn_notebook_id"/>
				<result column="cn_note_title" property="cn_note_title"/>
				<result column="cn_note_body" property="cn_note_body"/>
			</collection>
		</collection>
	</resultMap>
</mapper>

    6. 关于resultMap中的关联查询映射,实际上主要就是collection与association两个子标签的运用,这两个子标签之间可以互相嵌套,以应对要映射的类中包含pojo类型的属性或者是包含集合类型的属性,或者是属性的属性中包含pojo类型的属性或包含集合类型的属性,通过collection与association两个子标签的互相嵌套使用就可以解决具有复杂属性的映射类问题。如果过属性为集合就使用collection,如果为pojo类型就使用association,两者需要之间的使用区别就在于collection标签的属性中指定集合中元素映射的类型使用ofType,association标签的属性中指定映射的类型使用javaType。



2. 动态SQL

    1. 动态SQL即Mybatis对sql语句进行灵活操作,通过一些标签进行判断,对sql进行灵活拼接、组装。

    2. 条件判断标签<if test=""></if>:对输入的参数进行判断,如果输入参数满足条件才进行SQL语句拼接。如果传入的参数是一个包装对象,那么就通过属性.属性.属性··来对参数进行判断

<!--无判断sql-->
	<select id="findUserById" parameterType="entity.User" resultType="entity.User">
		select * from cn_user 
		where cn_user_id=#{id} 
		and cn_user_name=#{cn_user_name} 
		and cn_user_password=#{cn_user_password}
	</select>
<!--无判断添加if标签判断-->
	<select id="findUserById" parameterType="entity.User" resultType="entity.User">
		select * from cn_user 
		<!-- where标签相当于sql中的where关键字,但该标签还可以自动消除拼接的sql语句中第一个and、or关键字
		比如,如果cn_user_name为空那么就会拼接and cn_user_password=#{cn_user_password},and关键字是多余的
		而where标签就会消除and标签,而且还相当于where关键字
		 -->
		<where>
		<!-- 判断参数是否为空 -->
			<if test="cn_user_name!=null and cn_user_name!=''">
				and cn_user_name=#{cn_user_name}
			</if>
			<if test="cn_user_password!=null and cn_user_password!=''">
				and cn_user_password=#{cn_user_password}
			</if>
		</where>
	</select>

    3. SQL片段:将一些的频繁出现的sql代码块提取出来,成为一个SQL片段,可以提高SQL的复用性,其它的statement中就可以引用sql片段,方便程序员进行开发,通过标签<sql id=""></sql>定义,比如将上面的判断条件部分的提取为一个代码块

<!-- 通过id定义该SQL片段在当前Mapper文件中的唯一标示,通过该id就可以被其他sql语句引用
	一般来说SQL片段要基于单表,其次SQL片段中不要出现where关键字或者where标签,因为如果sql片段中
	有了一个where那么组合其他sql片段是就会出现错误 -->
	<sql id="query_if">
		<!-- 判断参数是否为空 -->
			<if test="cn_user_name!=null and cn_user_name!=''">
				cn_user_name=#{cn_user_name}
			</if>
			<if test="cn_user_password!=null and cn_user_password!=''">
				and cn_user_password=#{cn_user_password}
			</if>
	</sql>
	<select id="findUserById" parameterType="entity.User" resultType="entity.User">
		select * from cn_user 
		<where>
			<!-- 引用sql片段,如果引用的SQL片段在另一个mapper文件中,那么就要在SQL片段的id
            前面加上SQL片段所在的mapper文件的namespace,比如 usermapper.query_if -->
			<include refid="query_if"></include>
		</where>
	</select>

    4. 如果Mapper映射文件中sql语句的映射输入对象是一个包装对象,而且该包装对象的属性是一个List或者数组对象,可以通过foreach 标签来对该属性对象进行解析遍历:比如如下两种sql语句形式

第一种形式:select * from cn_user where cn_user_id=123 or cn_user_id=2342 or cn_user_id=34636
该形式中的where部分可变为如下SQL片段
		<where>
		<!-- 
			collection:指定输入映射对象中的List或数组类型的属性
			separator:指定遍历拼接的两个片段之间进行分隔 的关键字,比如and、or
			index:用来表示遍历的每个元素的下标,可以通过该index属性值来提取元素下标
			item:用来表示遍历的每个元素
			open:表示遍历拼接SQL片段之前要添加的SQL片段
			close:表示遍历拼接SQL片段之后要添加的SQL片段
		 -->
			<foreach collection="ids" separator="or" index="index" item="id" open="(" close=")">
				cn_user_id=#{id}<!--注意,这里的#{}中必须写与item指定的字符串相同-->
			</foreach>
		</where>

第二种形式:select * from cn_user where cn_user_id in (1,2,3,4)
该形式中的where部分可变为如下SQL片段
		<where>
			<foreach collection="ids" separator="," index="index" item="id" open="cn_user_id in (" close=")">
				#{id}<!--注意,这里的#{}中必须写与item指定的字符串相同-->
			</foreach>
		</where>

        <foreach collection="" separator="" index="" item="" open="" close=""></foreach>