1概述
1.1应用架构
mybatis框架用于支持对关系数据库的操做,该体系的应用架构以下图所示:java
在mybatis框架体系中,主要的组件是:SqlSessionFactoryBean和MapperScannerConfigurer。SqlSessionFactoryBean类依赖外部注入的数据源:DataSource。并有两个属性:configLocation和mapperLocations。
ConfigLocation指定了mybatis配置文件的位置;mapperLocations指定了多个mapper映射文件的位置。
MapperSacannerConfigurer依赖SqlSessionFactoryBean,并根据basePackages属性指定的IDao接口类所在的包位置,自动扫描该包路径,为每一个IDao类建立一个MapperFactoryBean类。MapperFactoryBean类会为它所对应的IDao接口类建立其具体的实现类实例,并将其注入到service层的业务代码中。
在Spring boot开发中,使用@MapperScan注解代替MapperScannerConfigurer类。它们的效果是同样的。
在使用mybatis框架实现数据库操做的时候,上图红色框部分的内容须要开发人员实现。
数据源是任何实现了javax.sql.datasource接口的类实例。例如:org.apache.tomcat.jdbc.pool.datasource,该数据源对应单库单表操做;或者com.dangdang.ddframe.rdb.sharding.jdbc.core.datasource.ShardingDataSource,该数据源对用分库,分表操做。
IDao为数据操做接口,实现用户的数据操做逻辑。通常分为接口类和po类两部分。
配置文件分为:mybatis配置文件和mapper配置文件。mybatis配置文件主要用来设计po类的别名,方便对po类的引用;mapper配置文件用来管理SQL语句。
1.2主要开发要素
在mybatis开发中,涉及到主要开发要素是:dao接口类,mapper映射文件,以及po类。它们之间的关系以下:sql
dao接口类中,定义了数据库操做的接口方法,主要包含增,删,改,查等接口方法;po类定义接口方法的参数,可以使用po类保存查询结果,或者为insert,update方法提供数据集参数。操做数据库表的SQL语句保存在mapper映射文件中。mapper映射文件分别提供select,insert,update,delete xml元素,分别对应数据库的查询,插入,修改,删除操做。每个xml元素经过id属性与dao接口类中的方法相互关联。数据库
1.3 mapper文件的结构
mapper映射文件是xml格式的配置文件,由一系列具备层级关系的元素组成。而且经过元素的属性,这些元素之间具备关联关系。具体状况以下图所示:apache
在mapper映射文件中,主要包含以下配置元素:tomcat
2 文件示例
在开发过程当中,须要开发人员配置mapper映射文件,编写Idao类,以及Idao了所依赖的po类。具体示例以下:
2.1 mapper映射文件
mapper配置文件的示例以下:mybatis
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org/DTD Mapper 3.0" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.lifeng.demo.mybatis.dao.IUserDao"> <resultMap type="UserInfo" id="userData"> <id property="id" column="f_id" /> <result property="name" column="f_name" /> <result property="birth" column="f_birth" /> <result property="salary" column="f_salary" /> </resultMap> <!-- id要与接口方法名相同 --> <!-- SQL语句中的参数名称(#{id}),要与java代码中的参数bean的数据字段相同,这里是UserInfo.id字段 --> <!-- type属性可省略 --> <insert id="insertUserInfoByBean"> insert into t_user (f_id,f_name,f_birth,f_salary) values (#{id},#{name},#{birth},#{salary}) </insert> <!-- @Param的参数必须与#{}中的参数一致 --> <insert id="insertUserInfo"> insert into t_user (f_id,f_name,f_birth,f_salary) values (#{id},#{name},#{birth},#{salary}) </insert> <insert id="insertUserInfoByBatch"> insert into t_user (f_id,f_name,f_birth,f_salary) values <foreach collection="list" item="item" separator="," index="idx"> (#{idx},#{item.name},#{item.birth},#{item.salary}) </foreach> </insert> <!--resultMap属性的值是 resultMap配置节id的值。当承载返回结果的java bean数据字段与数据库表字段格式不一致时,使用resultMap --> <!-- 若是返回多行数据,会用list封装UserData --> <select id="listUserInfo" resultMap="userData"> select * from t_user </select> <select id="getUserCount" resultType="int"> select count(*) from t_user </select> <select id="listUserInfoToMap" resultType="map"> select * from t_user </select> <select id="getUserInfoById" resultMap="userData"> select * from t_user where f_id = #{id} </select> <select id="getUserInfoToMap" resultType="hashmap"> select * from t_user where f_id=#{id} </select> <delete id="deleteAll"> delete from t_user </delete> <delete id="deleteUserInfoById"> delete from t_user where id=#{id} </delete> <update id="updateUserInfo"> update t_user set f_name = #{name} where f_id = #{id} </update> </mapper>
2.2 po文件
po文件的示例以下:架构
package com.lifeng.demo.mybatis.dao.dto; import java.io.Serializable; import java.text.SimpleDateFormat; import java.util.Date; public class UserInfo implements Serializable { private static final long serialVersionUID = 6730890636022435120L; private int id; private String name; private Date birth; private double salary; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Date getBirth() { return birth; } public void setBirth(Date birth) { this.birth = birth; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("UserInfo = ["); builder.append("id:"); builder.append(this.id); builder.append(" name:"); builder.append(this.name); if (this.birth != null) { builder.append(" birth:"); builder.append(new SimpleDateFormat("yyyyMMdd").format(this.birth)); } builder.append(" salary:"); builder.append(this.salary); return builder.toString(); } }
2.3 dao文件
Dao接口的示例以下:app
package com.lifeng.demo.mybatis.dao; import java.math.BigInteger; import java.util.Date; import java.util.List; import java.util.Map; import org.apache.ibatis.annotations.MapKey; import org.apache.ibatis.annotations.Param; import com.lifeng.demo.mybatis.dao.dto.UserInfo; public interface IUserDao { List<UserInfo> listUserInfo(); int getUserCount(); UserInfo getUserInfoById(int Id); Map<String,Object> getUserInfoToMap(int id); @MapKey("f_id") Map<BigInteger,Map<String,Object>> listUserInfoToMap(); int insertUserInfoByBean(UserInfo userInfo); int insertUserInfo(@Param("id") int id,@Param("name") String userName,@Param("birth") Date birthDay,@Param("salary") double salary); int insertUserInfoByBatch(List<UserInfo> ls); int updateUserInfo(UserInfo userInfo); int deleteUserInfoById(int Id); int deleteAll(); }
3 经常使用元素
3.1 resultMap
使用resultMap配置节,创建po类的数据字段与数据库表的列名之间的映射关系。当po类的数据字段与数据库表的列名不一致的时候,可以使用该配置。 resultMap的代码示例以下:框架
<resultMap type="UserInfo" id="userData"> <id property="id" column="f_id" /> <result property="name" column="f_name" /> <result property="birth" column="f_birth" /> <result property="salary" column="f_salary" /> </resultMap>
type属性执行po类的全类型名,即:包名+类名。若是在mybatis配置文件中,为po类创建了别名,那么type属性能够引用该别名。
id属性应该全局惟一,它被select元素的resultMap属性引用。ide
<id>子元素用来创建po数据字段与数据库表主键列之间的映射关系。
<result>子元素用来创建po数据字段与数据库表非主键列之间的映射关系。
property属性用来指定po类的数据字段名。例如:private String name;该定义中的name。
column属性用来指定数据库表的列名。
3.2 select
在mapper文件中,使用select元素来管理select语句。
3.2.1 返回单值
当查询结果返回一个单值的时候,其配置以下:
<select id="getUserCount" resultType="int"> select count(*) from t_user </select>
使用resultType属性指定返回结果的数据类型,这里能够是java基本数据类型,如:int,String等。
在IDao类中,接口方法定义以下:
int getUserCount();
方法的返回值类型是int,与resultType的值对应;方法的名称是getUserCount与id属性的值对应。
3.2.2 返回单对象
单对象的含义是:查询结果包含多个数据列,但只有一行数据。可使用javabean或者map来保存查询结果。
3.2.2.1经过javabean返回单对象
经过javabean返回查询结果的配置以下:
<select id="getUserInfoById" resultMap="userData"> select * from t_user where f_id = #{id} </select>
当po类的数据字段与数据库表的列名彻底一致的时候,可以使用resultType=po类全类型名,或po类别名 的方式指定查询结果的类型。
当po类的数据字段与数据库表的列名不一致的时候,须要定义resultMap映射。在这种状况下,使用resultMap属性,该属性的值与<resultMap>元素的id属性的值相同。
resultMap与resultType不能同时使用。
在IDao类中,接口方法的定义以下:
UserInfo getUserInfoById(int id)
返回值的类型是UserInfo,经过<resultMap>元素的配置,将该类型映射到userData。在<select>元素中,经过resultMap属性引用userData.
3.2.2.2经过map返回单对象
当查询结果是一个单对象,可是没有定义po类的时候,可以使用map来承载查询结果。map的key是数据库表的列名,value是该列对应的值。mapper映射文件的配置以下:
<select id="getUserInfoToMap" resultType="hashmap"> select * from t_user where f_id=#{id} </select>
resultType属性的值设定为hashmap,或者map便可。IDao类中,接口方法定义以下:
Map<String,Object> getUserInfoToMap(int id);
3.2.3返回数据集
数据集的含义是:包含多行数据,每行数据包含多列。
3.2.3.1经过list返回数据集
当查询结果返回一个数据集的时候,mybatis会将查询结果放入list中,而后返回。在这种状况下,resultTpye或者resultMap属性指定的是list列表元素的类型,而非集合自己。mapper映射文件的配置以下:
<select id="listUserInfo" resultMap="userData"> select * from t_user </select>
resultMap或者resultType能够设定为java bean ,也可设定为map。该配置形式与返回单对象相同,其差异在IDao类中的接口方法定义:
list<UserInfo> listUserInfo();
在定义接口方法的时候,须要使用list包装单值对象的类型。
3.2.3.2经过map返回数据集
该方式须要与注解@MapKey配合使用。map的key是:数据库表中的任意一列,通常选具备索引性质的列,如:id,name等。value是:po类的引用,或者另一个map的引用。mapper配置文件的内容以下:
<select id="listUserInfoToMap" resultType="map"> select * from t_user </select>
在IDao类中,接口方法的定义以下:
@MapKey("f_id")
Map<BigInteger,Map<String,Object>> listUserInfoToMap();
在上面的示例中,value的值是map类型。value的值也能够是po类。在使用po类的时候,mapper映射文件的配置以下:
<select id="listUserInfo" resultMap="userData"> select * from t_user </select>
在IDao类中,接口方法定义以下:
@MapKey("id") map<int,UserInfo> listUserInfo();
3.3结论
resultType使用汇总以下,resultType属性的值能够是以下情形:
1.基本数据类型,如:int,String等;
2.class数据类型,如:java bean,这里输入的是全类型名或者别名;
3.map数据类型。包括:单对象和集合两种;
4.集合数据类型,是集合元素的类型,而非集合自己。
resultMap的使用汇总以下:
该属性的值是:<resultMap>元素的id属性的值。只有当po数据字段与数据库表列名不一致的时候,才使用。
3.3 insert
在mybatis中,使用<insert>元素来管理insert语句。若是sql语句比较简单,可以使用@Insert注解来代替mapper映射文件。
若是须要插入的数据字段比较多,可使用po类封装这些数据字段,示例以下:
<insert id="insertUserInfoByBean" parameterType="UserInfo">
insert into t_user (f_name,f_birth,f_salary) values (#{name},#{birth},#{salary})
</insert>
IDao类的接口方法定义以下:
int insertUserInfoByBean(UserInfo userInfo);
<insert>元素的id属性值是IDao类接口方法的名称。
<insert>元素的parameterType属性值能够省略。其值是接口方法参数的类型。若是接口方法的参数是java bean,那么该值是全类型名(即:包名+类名),或者是类型的别名。
注意:在sql语句中,如:#{name},#{birth}等参数,其名称:name,birth,必须与接口参数UserInfo的数据字段一致。即:UserInfo的数据字段名称必须是name,birth。不然没法识别。
若是须要插叙的数据字段比较少,那么能够直接传递给参数到接口方法中,mapper映射文件的示例以下:
<insert id="insertUserInfo" >
insert into t_user (f_name,f_birth,f_salary) values (#{name},#{birth},#{salary})
</insert>
IDao接口类的定义以下:
int insertUserInfo(@Param("name") String userName,@Param("birth") Date birthDay,@Param("salary") double salary);
注解@Param用来指定接口方法的参数名与sql语句中的参数名之间的映射。@Param注解的参数,如:name,必须与sql语句中的参数,如:#{name}中的name 是彻底一致的。
若是sql语句比较简单,可使用注解来代替mapper映射文件,示例以下:
@Insert("{insert into t_user (f_name,f_birth,f_salary) values (#{name},#{birth},#{salary})}") int insertUserInfo(@Param("name") String userName,@Param("birth") Date birthDay,@Param("salary") double salary);
3.4 update
<update>元素用来维护update类型的sql语句。若是该sql语句比较简单,可使用@Update注解代替mapper映射文件。
3.5 delete
<delete>元素用来维护delete类型的sql语句。若是该sql语句比较简单,可使用@Delete注解来代替mapper映射文件。
3.6 动态sql
使用动态sql来支持复杂的sql语句。在动态sql部分,包含以下xml元素:
if, choose, when, otherwise, trim, where, set, foreach。
3.6.1示例表
以以下表结构示例动态sql:
3.6.2 if语句
根据 username 和 sex 来查询数据。若是username为空,那么将只根据sex来查询;反之只根据username来查询。首先不使用 动态SQL 来书写
<select id="selectUserByUsernameAndSex" resultType="user" parameterType="com.ys.po.User"> <!-- 这里和普通的sql 查询语句差很少,对于只有一个参数,后面的 #{id}表示占位符,里面不必定要写id, 写啥均可以,可是不要空着,若是有多个参数则必须写pojo类里面的属性 --> select * from user where username=#{username} and sex=#{sex} </select>
上面的查询语句,咱们能够发现,若是 #{username} 为空,那么查询结果也是空,如何解决这个问题呢?使用 if 来判断
<select id="selectUserByUsernameAndSex" resultType="user" parameterType="com.ys.po.User"> select * from user where <if test="username != null"> username=#{username} </if> <if test="username != null"> and sex=#{sex} </if> </select>
这样写咱们能够看到,若是 sex 等于 null,那么查询语句为 select * from user where username=#{username},可是若是usename 为空呢?那么查询语句为 select * from user where and sex=#{sex},这是错误的 SQL 语句,如何解决呢?请看下面的 where 语句
3.6.3 if + where 语句
<select id="selectUserByUsernameAndSex" resultType="user" parameterType="com.ys.po.User"> select * from user <where> <if test="username != null"> username=#{username} </if> <if test="username != null"> and sex=#{sex} </if> </where> </select>
这个“where”标签会知道若是它包含的标签中有返回值的话,它就插入一个‘where’。此外,若是标签返回的内容是以AND 或OR 开头的,则它会剔除掉。
3.6.3 if + set 语句
同理,上面的对于查询 SQL 语句包含 where 关键字,若是在进行更新操做的时候,含有 set 关键词,咱们怎么处理呢?
<!-- 根据 id 更新 user 表的数据 --> <update id="updateUserById" parameterType="com.ys.po.User"> update user u <set> <if test="username != null and username != ''"> u.username = #{username}, </if> <if test="sex != null and sex != ''"> u.sex = #{sex} </if> </set> where id=#{id} </update>
这样写,若是第一个条件 username 为空,那么 sql 语句为:update user u set u.sex=? where id=? ; 若是第一个条件不为空,那么 sql 语句为:update user u set u.username = ? ,u.sex = ? where id=?
3.6.4 choose(when,otherwise) 语句
有时候,咱们不想用到全部的查询条件,只想选择其中的一个,查询条件有一个知足便可,使用 choose 标签能够解决此类问题,相似于 Java 的 switch 语句
<select id="selectUserByChoose" resultType="com.ys.po.User" parameterType="com.ys.po.User"> select * from user <where> <choose> <when test="id !='' and id != null"> id=#{id} </when> <when test="username !='' and username != null"> and username=#{username} </when> <otherwise> and sex=#{sex} </otherwise> </choose> </where> </select>
也就是说,这里咱们有三个条件,id,username,sex,只能选择一个做为查询条件
若是 id 不为空,那么查询语句为:select * from user where id=?
若是 id 为空,那么看username 是否为空,若是不为空,那么语句为 select * from user where username=?;
若是 username 为空,那么查询语句为 select * from user where sex=?
3.6.5 trim语句
trim标记是一个格式化的标记,能够完成set或者是where标记的功能
①、用 trim 改写上面第二点的 if+where 语句
<select id="selectUserByUsernameAndSex" resultType="user" parameterType="com.ys.po.User"> select * from user <!-- <where> <if test="username != null"> username=#{username} </if> <if test="username != null"> and sex=#{sex} </if> </where> --> <trim prefix="where" prefixOverrides="and | or"> <if test="username != null"> and username=#{username} </if> <if test="sex != null"> and sex=#{sex} </if> </trim> </select>
prefix:前缀;prefixoverride:去掉第一个and或者是or。
②、用 trim 改写上面第三点的 if+set 语句
<!-- 根据 id 更新 user 表的数据 --> <update id="updateUserById" parameterType="com.ys.po.User"> update user u <!-- <set> <if test="username != null and username != ''"> u.username = #{username}, </if> <if test="sex != null and sex != ''"> u.sex = #{sex} </if> </set> --> <trim prefix="set" suffixOverrides=","> <if test="username != null and username != ''"> u.username = #{username}, </if> <if test="sex != null and sex != ''"> u.sex = #{sex}, </if> </trim> where id=#{id} </update>
suffix:后缀
suffixoverride:去掉最后一个逗号(也能够是其余的标记,就像是上面前缀中的and同样)
3.6.6 SQL片断
有时候可能某个 sql 语句咱们用的特别多,为了增长代码的重用性,简化代码,咱们须要将这些代码抽取出来,而后使用时直接调用。
好比:假如咱们须要常常根据用户名和性别来进行联合查询,那么咱们就把这个代码抽取出来,以下:
<!-- 定义 sql 片断 --> <sql id="selectUserByUserNameAndSexSQL"> <if test="username != null and username != ''"> AND username = #{username} </if> <if test="sex != null and sex != ''"> AND sex = #{sex} </if> </sql>
引用 sql 片断
<select id="selectUserByUsernameAndSex" resultType="user" parameterType="com.ys.po.User"> select * from user <trim prefix="where" prefixOverrides="and | or"> <!-- 引用 sql 片断,若是refid 指定的不在本文件中,那么须要在前面加上 namespace --> <include refid="selectUserByUserNameAndSexSQL"></include> <!-- 在这里还能够引用其余的 sql 片断 --> </trim> </select>
注意:①、最好基于 单表来定义 sql 片断,提升片断的可重用性
②、在 sql 片断中不要包括 where
3.6.7 foreach语句
需求:咱们须要查询 user 表中 id 分别为1,2,3的用户
sql语句:select * from user where id=1 or id=2 or id=3
select * from user where id in (1,2,3)
①、创建一个 UserVo 类,里面封装一个 List<Integer> ids 的属性
package com.ys.vo; import java.util.List; public class UserVo { //封装多个用户的id private List<Integer> ids; public List<Integer> getIds() { return ids; } public void setIds(List<Integer> ids) { this.ids = ids; } }
②、咱们用 foreach 来改写 select * from user where id=1 or id=2 or id=3
<select id="selectUserByListId" parameterType="com.ys.vo.UserVo" resultType="com.ys.po.User"> select * from user <where> <!-- collection:指定输入对象中的集合属性。该属性的值有三种:list,array,map,根据传入的集合类型而设定该值。 item:每次遍历生成的对象 index:当前迭代的次数 open:开始遍历时的拼接字符串 close:结束时拼接的字符串 separator:遍历对象之间须要拼接的字符串 select * from user where 1=1 and (id=1 or id=2 or id=3) --> <foreach collection="list" item="id" open="and (" close=")" separator="or"> id=#{id} </foreach> </where> </select>
③、咱们用 foreach 来改写 select * from user where id in (1,2,3)
<select id="selectUserByListId" parameterType="com.ys.vo.UserVo" resultType="com.ys.po.User"> select * from user <where> <!-- collection:指定输入对象中的集合属性.该属性的值有三种:list,array,map,根据传入的集合类型而设定该值。 item:每次遍历生成的对象 index:当前迭代的次数 open:开始遍历时的拼接字符串 close:结束时拼接的字符串 separator:遍历对象之间须要拼接的字符串 select * from user where 1=1 and id in (1,2,3) --> <foreach collection="list" item="id" open="and id in (" close=") " separator=","> #{id} </foreach> </where> </select>