MyBatis
MyBatis 是支持普通 SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索。MyBatis 使用简单的 XML或注解用于配置和原始映射,将接口和 Java 的POJOs(Plain Ordinary Java Objects,普通的 Java对象)映射成数据库中的记录。
每个MyBatis应用程序主要都是使用SqlSessionFactory实例的,一个SqlSessionFactory实例可以通过SqlSessionFactoryBuilder获得。SqlSessionFactoryBuilder可以从一个xml配置文件或者一个预定义的配置类的实例获得。
配置文件
Spring集成MyBatis(数据源使用druid)配置文件 applicationContext.xml。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<!-- 开启注解扫描 -->
<context:component-scan base-package="com.hu" />
<!-- 配置数据源 -->
<bean id="dataSource"
class="com.alibaba.druid.pool.DruidDataSource" init-method="init"
destroy-method="close">
<!-- ”连接“的基本属性 -->
<property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatisdemo?useUnicode=true&characterEncoding=utf-8" />
<property name="username" value="root" />
<property name="password" value="123456" />
<!-- 连接池属性 -->
<!-- 初始化连接池个数 -->
<property name="initialSize" value="5" />
<!-- 最大连接池个数 -->
<property name="maxActive" value="20" />
<!-- 配置获取连接等待超时的时间,单位毫秒 -->
<property name="maxWait" value="60000" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="300000" />
<property name="keepAlive" value="true" />
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="-1" />
<!-- 最小连接池个数 -->
<property name="minIdle" value="20" />
<!-- <property name="removeAbandoned" value="true" />
<property name="removeAbandonedTimeout" value="180" />
<property name="logAbandoned" value="true" /> -->
<!-- 申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 -->
<property name="testWhileIdle" value="true" />
<!-- 用来检测连接是否有效的sql,要求是一个查询语句 -->
<!-- 如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用 -->
<property name="validationQuery" value="SELECT 1" />
<!-- 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能 -->
<property name="testOnBorrow" value="false" />
<!-- 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能 -->
<property name="testOnReturn" value="false" />
<!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->
<property name="poolPreparedStatements" value="true" />
<!-- 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。-->
<!-- 在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100 -->
<property name="maxPoolPreparedStatementPerConnectionSize" value="20" />
<!-- 通过别名的方式配置扩展插件,多个英文逗号分隔 -->
<!-- 监控统计用的filter:stat -->
<!-- 日志用的filter:log4j -->
<!-- 防御sql注入的filter:wall -->
<property name="filters" value="stat,wall,log4j" />
<!-- 通过connectProperties属性来打开mergeSql功能;慢SQL记录 -->
<property name="connectionProperties"
value="druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000" />
<!-- 合并多个DruidDataSource的监控数据 -->
<!-- <property name="poolPreparedStatements" value="true" /> -->
</bean>
<!-- 会话工厂sqlSessionFactoryBean -->
<bean id="sqlSessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 数据源 -->
<property name="dataSource" ref="dataSource"></property>
<!-- 别名 -->
<property name="typeAliasesPackage"
value="com.hu.pojo"></property>
<!-- sql映射文件路径 -->
<property name="mapperLocations"
value="classpath:mapper/*Mapper.xml"></property>
</bean>
<!-- 自动扫描对象关系映射 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--指定会话工厂,如果当前上下文中只定义了一个则该属性可省去 -->
<property name="sqlSessionFactoryBeanName"
value="sqlSessionFactory"></property>
<!-- 指定要自动扫描接口的基础包,实现接口 -->
<property name="basePackage"
value="com.hu.mapper"></property>
</bean>
<!-- 声明式事务管理 -->
<!--定义事物管理器,由spring管理事务 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--支持注解驱动的事务管理,指定事务管理器 -->
<tx:annotation-driven
transaction-manager="transactionManager" />
<!-- aspectj支持自动代理实现AOP功能 -->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
</beans>
在配置文件中,需要指定Mapper接口和Mapper.xml文件的位置(包名).
Mapper
传值
UserMapper.java
public interface UserMapper {
//封装对象
int insert(@Param("user")User user);
//集合
int insertList(@Param("list")List<User> list);
//单个String
List<User> selectList(String condition);
//Map对象
int method1(Map<String, Object> map);
}
Mapper接口传参到UseMapper.xml的方式主要有几种:
- 单个基本数据类型或String类型的参数(如:id),xml文件中直接使用#{}或${}引用。
- 多个参数,使用@Param(““)注解分辨(如:id,name),xml中使用#{} #{}或${} ${}引用。
- 封装对象(单个或多个),使用@Param(““)注解标明,xml中使用对象
.
属性引用(如:#{user.id} ${user.name})。若想直接使用#{属性
}或${属性
}取值,则在Mapper接口中传入对象时,不能使用@Param(““)注解标注(否则会造成参数不匹配错误,导致操作失败)。 - Map对象,使用Map对象无需使用@Param(““)注解。xml中使用对象
.
属性引用(如:#{wang.id} ${li.name})
取值
UserMapper.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.hu.mapper.UserMapper">
<insert id="insert">
insert into user(username,password) values(#{user.username},#{user.password});
<!-- insert into user(username,password) values("${user.username}","${user.password}") -->
</insert>
<insert id="insertList">
insert into user(username,password) values
<foreach collection="list" item="user" separator="," index="index">
(#{user.username},#{user.password})
</foreach>
</insert>
<select id="selectList" resultType="user">
select * from user
<if test="condition != null and condition != ''">
where username = #{condition}
</if>
</select>
<insert id="method1">
insert into user(username,password)
values(#{li.username},#{li.password}),
(#{wang.username},#{password})
</insert>
</mapper>
在xml文件中,使用 #{}
和 ${}
取参数值。它们的主要区别是:
${}
直接将参数值(原封不动的)放到SQL语句的对于位置。
#{}
会根据参数值的类型,自动加上""
。若取得的参数是String、char类型,会自动在该参数前后加上"
。
<!--接口传入 id = 1,username = "hu" -->
<!--使用#{} -->
select * from user where id = #{id} and username = #{username}
<!-- 该语句等价于 -->
select * from user where id = 1 and username = "hu"
<!--使用${} -->
select * from user where id = ${id} and username = "${username}
<!-- 该语句等价于 -->
select * from user where id = 1 and username = "hu"
使用 ${}
并不会自动添加 ""
,需要自己手动添加(若没有手动添加,在遇到String或char类型参数时会出错)。在使用模糊查询时,不能使用#{}
。#{}
是将内部的值(即使拼接后是一条语句)都作为一个参数,所以使用 #{}
可以防止SQL注入。
<!--接口传入 username = "hu" -->
<!--模糊查询 -->
<!--使用#{} -->
select * from user where username like "%#{}%"
<!-- 该语句等价于 -->
select * from user where username like "%"hu"%"
<!-- 执行会出错 -->
<!--使用${} -->
select * from user where username like "%${}%"
<!-- 该语句等价于 -->
select * from user where username like "%hu%"
标签
定义SQL语句
<sql>
用来定义常用sql语句,配合<include>
标签使用。
<include>
引用定义好的sql语句。
<sql id="selectAll">select *</sql>
<select id="select" resultType="user">
<include refid="selectAll"/>
from user where id = #{id}
</select>
<insert>
用来定义INSERT插入语句。默认返回int值(该条语句执行后影响的行数)。
<insert id="insert">
insert into user(username,password) values(#{user.username},#{user.password});
</insert>
<update>
用来定义UPDATE更新语句。默认返回int值(该条语句执行后影响的行数)。
<update id="update">
update user set password = #{password}
</update>
<delete>
用来定义DELETE删除语句。默认返回int值(该条语句执行后影响的行数)。
<delete id="delete">
delete from user where id = #{id}
</delete>
<select>
用来定义SELECT查询语句。需要指定 resultType
或 resultMap
属性值。
<select id="select" resultType="user">
select * from user where id = #{id}
</select>
属性:
parameterType:传给此语句的参数的全路径名或别名 例:com.hu.pojo.User或user
resultType:语句返回值类型或别名(映射配置文件中会话工厂配置的类型别名)。
resultMap:语句返回值类型或别名(自定义对象属性加载,如需查询对象中包含了别的对象或数组,需使用resultMap)。
定义返回类型
<resultMap>
建立SQL查询结果字段与实体属性的映射关系信息;将结果集中的列与java对象中的属性对应起来并将值填充进去。resultMap: type association:javaType collection:ofType
<resultMap id="userMap" type="user">
<id property="id" column="u_id" />
<result property="username" column="u_username" />
<result property="password" column="u_password" />
<association property="role" javaType="role">
<id property="id" column="r_id" />
<result property="name" column="r_name" />
<collection property="menus" ofType="menu">
<id property="id" column="m_id" />
<result property="name" column="m_name" />
</collection>
</association>
</resultMap>
<select id="selectUser" resultMap="userMap">
select u.id as u_id,u.username as u_username,u.password as u_password,
r.id as r_id,r.name as r_name,
m.id as m_id,m.name as m_name
from user u
left join user_role ur
on u.id = ur.user_id
left join role r
on r.id = ur.role_id
left join role_menus rm
on rm.role_id = r.id
left join menu m
on m.id = rm.menu_id
</select>
属性:
id:定义的resultMap的id,用于引用。
type:语句返回值类型或别名(映射配置文件中会话工厂配置的类型别名)。
<id>
id的唯一作用就是在嵌套的映射配置时判断数据是否相同,当配置id标签时,MyBatis只需要逐条比较所有数据中id标签字段值是否相同即可,可以提高处理效率。property对应Java对象的属性,column对象查询集中的列名。
<result>
用于将查询结果集和Java对象的属性一一映射起来,将结果集中的列赋值给对应的对象属性。property对应Java对象的属性,column对象查询集中的列名。没有配置result的属性不会被赋值。
<association>
与result标签类似,用于映射Java对象中的单一对象属性(复杂类型属性)。property对应该单一对象属性的名称;javaType对应该对象属性的Java类型(类似于select标签的type属性)。
<collection>
与collection标签类似,用于映射Java对象中的集合属性(如List)。property对应该集合属性的名称;ofType对应该集合属性中的Java类型(类似于association标签的javaType属性)。
SQL语句动态拼接
<foreach>
foreach标签主要用于遍历集合,构建in条件语句或者批量操作语句。他可以在sql中对集合进行迭代。
<insert id="insertList">
insert into user(username,password) values
<foreach collection="list" item="user" separator="," index="index">
(#{user.username},#{user.password})
</foreach>
</insert>
属性:
collection:表示迭代集合的名称,可以使用@Param注解指定,该参数为必选。
item:表示本次迭代获取的元素,若collection为List、Set或者数组,则表示其中的元素;若collection为map,则代表key-value的value,该参数为必选。
open:表示该语句以什么开始,最常用的是左括弧 (
,注意:mybatis会将该字符拼接到整体的sql语句之前,并且只拼接一次,该参数为可选项。
close:表示该语句以什么结束,最常用的是右括弧 )
,注意:mybatis会将该字符拼接到整体的sql语句之后,该参数为可选项。
separator:mybatis会在每次迭代后给sql语句加上separator属性指定的字符,该参数为可选项。
index:用于获取当前迭代的位置。在list、Set和数组中,index表示当前迭代的位置,在map中,index代指是元素的key,该参数是可选项。
<if>
if标签通常用于WHERE语句中,通过判断参数值来决定是否执行其中的语句, 他也经常用于UPDATE语句中判断是否更新某一个字段,还可以在INSERT语句中用来判断是否插入某个字段的值。
<select id="selectList" resultType="user">
select * from user
<if test="condition != null and condition != ''">
where username like "%${condition}%"
</if>
</select>
属性:
test:必填。该属性值是一个判断表达式,一般只用true或false作为结果。
<choose>
按顺序判断when中的条件出否成立,如果有一个成立,则choose结束。当choose中所有when的条件都不满则时,则执行 otherwise中的sql。类似于Java 的switch 语句,choose为switch,when为case,otherwise则为default。
<select id="selectChoose" resultType="user">
select * from user
<choose>
<when test="name != null and name != ''">
where username = #{name}
</when>
<otherwise>
where password = "5514"
</otherwise>
</choose>
</select>
<when>
与if标签类似,当 when 中的条件满足的时候就输出其中的内容。当 when 中有条件满足的时候,就会跳出 choose,即所有的 when 和 otherwise 条件中,只有一个会输出,
<otherwise>
当choose标签中的所有when标签都不满足条件时,执行otherwise中的内容。
格式化SQL语句
<where>
where 元素的作用是会在写入 where 元素的地方输出一个 where;如果所有的条件都不满足就会查出所有的记录;如果标签返回的内容是以 AND 或OR 开头的,则它会自动剔除掉(防止造成 where and 或者 where or 语法错误)。
<select id="selectWhere" resultType="user">
select * from user
<where>
<if test="username != null and username != ''">
username = #{username}
</if>
<if test="password != null and password != ''">
and password = #{password}
</if>
</where>
</select>
<set>
当 update 语句中没有使用 if 标签时,如果有一个参数为 null,都会导致错误。当在 update 语句中使用if标签时,如果前面的if没有执行,则或导致逗号多余错误。使用set标签可以将动态的配置 SET 关键字,并剔除追加到条件末尾的任何不相关的逗号。使用 if+set 标签修改后,若某一条if标签中的test不满足,则该条if标签中的语句不会执行。
<update id="updateSet">
update user
<set>
<if test="username != null and username != ''">
username = #{username},
</if>
<if test="password != null and password != ''">
password = #{password},
</if>
</set>
where id = #{id}
</update>
上述例子中,若两个if标签都不满足,则会造成语句错误(update user where id = ?);
<trim>
trim标签是一个格式化的标签,可以完成set或where标签的功能。与where和set标签类似,会根据prefixOverrides和suffixOverrides属性自动完成前缀和后缀的去除。
<select id="selectTrim" resultType="user">
select * from user
<trim prefix="where" prefixOverrides="and|or">
<if test="username != null and username != ''">
and username = #{username}
</if>
<if test="password != null and password != ''">
or password = #{password}
</if>
</trim>
</select>
<update id="updateTrim">
update user
<trim prefix="set" suffixOverrides=",">
<if test="username != null and username != ''">
username = #{username},
</if>
<if test="password != null and password != ''">
password = #{password},
</if>
</trim>
where id = #{id}
</update>
属性:
prefix:前缀覆盖并增加其内容
suffix:后缀覆盖并增加其内容
prefixOverrides:前缀判断的条件
suffixOverrides:后缀判断的条件