1、简介
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了手动 JDBC 代码、手动设置参数、获取结果集。Sql语句放在 XML文件 或注解来配置和映射原生信息。
使用的人多!公司需要!但是应为用了反射,效率会下降,所有有些公司会使用原生的jdbc
1.1、优缺点
sql语句与代码分离,存放于xml配置文件中:
优点:便于维护管理,不用在java代码中找这些语句;
缺点: JDBC方式可以用打断点的方式调试,但是Mybatis不能,需要通过log4j日志输出日志信息帮助调试,然后在配置文件中修改。
用逻辑标签控制动态SQL的拼接:
优点:用标签代替编写逻辑代码;
缺点:拼接复杂SQL语句时,没有代码灵活,拼写比较复杂。不要使用变通的手段来应对这种复杂的语句。
查询的结果集与java对象自动映射:
优点:保证名称相同,配置好映射关系即可自动映射或者,不配置映射关系,通过配置列名=字段名也可完成自动映射。
缺点:对开发人员所写的SQL依赖很强。
编写原生SQL:
优点:接近JDBC,比较灵活。
缺点:对SQL语句依赖程度很高;并且属于半自动,数据库移植比较麻烦,比如mysql数据库编程Oracle数据库,部分的sql语句需要调整。
1.2、Maven依赖
<dependencies>
<!-- 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
<!-- mybatis 核心 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<!-- 数据库确定 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
</dependencies>
1.3、MyBatis核心配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--数据源-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/ssm?useSSL=true&useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
<!-- 下划线转驼峰式 -->
<setting name="cacheEnabled" value="true"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="logPrefix" value="sql."/>
</settings>
<!--设置别名-->
<typeAliases>
<package name="com.xinzhi.entity"/>
</typeAliases>
<!--注册Mapper-->
<mappers>
<mapper resource="mappers/UserMapper.xml"/>
</mappers>
</configuration>
<?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.xinzhi.dao.UserMapper">
<select id="selectUser" resultType="com.xinzhi.entity.User">
select id,username,password from user
</select>
</mapper>
2、测试SQL
2.1、xml文件
useGeneratedKeys=“true” 开启返回主键ID
keyProperty=“id” 实体类字段
keyColumn=“id” 数据库字段
public interface UserMapper {
//根据id查询用户
User selectUserById(int id);
}
<!--id方法名,parameterType参数类型,resultType返回值类型,#{}占位符,${}字符串拼串-->
<select id="selectUserById" resultType="com.xinzhi.entity.User" parameterType="int">
select u_id , u_name , u_password from user where u_id = #{id}
</select>
2.2、注解
@Select("select u_id , u_name , u_password from user where u_id = #{id}")
public interface UserMapper {
//根据id查询用户
User selectUserById(int id);
}
2.3、模糊查询
<select id=”getUsersByName”>
select * from user where name like "%"#{name}"%"
</select>
<select id=”getUsersByName”>
select * from user where name like concat('%', #{username}, '%')
</select>
2.4、@Param
传递单个参数时,不使用Param。多个参数时,需要使用Param确认属性,除了Param,还可以使用Map
public interface UserMapper {
//根据id修改用户名
User selectUserById(@Param("id") int id,@Param("name")String name){
};
}
3、ResultMap
3.1、解决数据库列名和字段名不一致
<resultMap id="UserMap" type="User">
<!-- id为主键 -->
<id column="u_id" property="id"/>
<!-- column是数据库表的列名 , property是对应实体类的属性名 -->
<result column="u_name" property="name"/>
<result column="u_password" property="password"/>
</resultMap>
<select id="selectUserById" resultMap="UserMap">
select u_id , u_name , u_password from user where u_id = #{id}
</select>
3.2、多表维护
1、多
在多的表维护比如查询员工的信息,要把他的部门信息从另一张表查出来,返回的是对象
//两张表,两个类
public class Dept {
private int deptId;
private String deptName;
}
public class Employee {
private int emId;
private String emName;
//维护关系,存放,查询部门信息
private Dept dept;
}
<!--java类中维护关系的属性与表中的外键列名关联,查询完后拿到数据,再跟一条sql查询父表的数据-->
<resultMap id="EmployeeDept" type="com.xinzhi.entity.Employee">
<!--association关联属性 property属性名 javaType属性类型 column在多的一方的表中的列名-->
<association property="dept" column="dept_id" javaType="com.xinzhi.entity.Dept" select="getDept"/>
</resultMap>
<select id="findAll" resultMap="EmployeeDept">
select * from employee
</select>
<select id="getDept" resultType="com.xinzhi.entity.Dept" parameterType="int">
select * from dept where dept_id = #{id}
</select>
2、一
查询部门信息时,把所属部门的员工信息也查出来 ,返回的是一个集合
JavaType和ofType都是用来指定对象类型的,但是JavaType是用来指定pojo中属性的类型,而ofType指定的是
映射到list集合属性中pojo的类型
public class Dept {
private int deptId;
private String deptName;
//维护关系的字段,存员,查询工信息
private List<Employee> employees;
}
<resultMap id="DeptEmployee" type="com.xinzhi.entity.Dept">
<id column="dept_id" property="deptId"/>
<!--column是一对多的外键 , 写的是一的主键的列名-->
<collection property="employees" javaType="ArrayList"ofType="com.xinzhi.entity.Employee" column="dept_id"
select="getEmployee"/>
</resultMap>
<select id="findDeptById" resultMap="DeptEmployee">
select * from dept where dept_id = #{id}
</select>
<select id="getEmployee" resultType="com.xinzhi.entity.Employee">
select em_id,em_name from employee where dept_id = #{id}
</select>
3、懒加载
只有在连表查询的时候才有用。只有在使用维护数据时才会发sql去查询,提高了效率。默认是不开启的
<settings>
<!--懒加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
4、动态sql
元素 | 作用 | 备注 |
---|---|---|
if | 判断语句 | 单条件分支判断 |
choose、when、otherwise | 相当于Java中的 case when语句 | 多条件分支判断 |
trim、where、set | 辅助元素 | 用于处理一些SQL拼装问题 |
foreach | 循环语句 | 在in语句等列举条件常用 |
添加
prefix代表的是语句的前缀,而prefixOverrides代表的是你需要去掉的那种字符串,
suffix表示语句的后缀,suffixOverrides代表去掉的后缀字符串
<insert id="insertUser" parameterType="user">
insert into user
(
<trim suffixOverrides=",">
<if test="username != null and username != ''">
username,
</if>
<if test="password != null and password != ''">
password,
</if>
</trim>
)
values
(
<trim suffixOverrides=",">
<if test="username != null and username != ''">
#{username},
</if>
<if test="password != null and password != ''">
#{password},
</if>
</trim>
)
</insert>
多条件查询
<select id="selectUsersWhere" parameterType="user" resultType="user">
select id,username,password from user
<where>
<!--只有在非空且非空串的时候才有值,查询时可以传空,全空就查到所有-->
<if test="id != null and id != ''">
id = #{id}
</if>
<if test="username != null and username != ''">
And username LIKE concat('%', #{username}, '%')
</if>
<if test="password != null and password != ''">
And password = #{password}
</if>
</where>
</select>
根据一堆id查询
<select id="selectUserByIds" resultType="user" parameterType="int">
SELECT * FROM user
WHERE id IN
<!--ids传进来的数组,open开始给一个(,close关闭给一个),separator用,隔开,将ids遍历成一个个id-->
<foreach collection="ids" open="(" close=")" separator="," item="id">
#{id}
</foreach>
</select>
修改
在update语句中,如果我们只想更新某几个字段的值,这个时候可以使用set元素配合if元素来完成。注意:set元素遇到,会自动去掉
<update id="updateUser" parameterType="user">
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>
根据一堆id删除
<delete id="deleteUserByIds" parameterType="int">
delete FROM user
WHERE id IN
<foreach collection="ids" open="(" close=")" separator="," item="id">
#{id}
</foreach>
</delete>
5、日志
可以查看发出的sql,更方便的调试
5.1、标准日志
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
5.2、log4j
maven依赖
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
编写 配置文件log4j.properties
#将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
log4j.rootLogger=DEBUG,console,file
#控制台输出的相关设置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
#文件输出的相关设置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/kuang.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
#日志输出级别
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
mybatis.xml
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
6、缓存
- 如果缓存中有数据,就不用从数据库获取,大大提高系统性能
- mybatis提供一级缓存和二级缓存
一级缓存
- 一级缓存在mybatis中默认开启,是sqlsession级别的缓存。
- 在操作数据库时,需要构造sqlsession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据
- 不同的sqlsession之间的缓存区域是互相不影响的
- 在同一sqlsession中发送同一sql,不会重复发送而是直接从缓存中拿
- 如果sqlsession执行了commit操作(插入,更新,删除),会清空sqlsession中的一级缓存,避免脏读
缓存失效
- sqlSession不同
- 当sqlSession对象相同的时候,查询的条件不同
- 当sqlSession对象相同,两次查询之间进行了插入的操作
- 当sqlSession对象相同,手动清除了一级缓存中的数据
二级缓存
-
二级缓存是mapper级别的,不同sqlsession共用二级缓存
-
需要commit
-
二级缓存需要开启
<settings> <!--开启二级缓存--> <setting name="cacheEnabled" value="true"/> </settings> <!--需要开启本Mapper的namespace下的二级缓存--> <!--LRU(Least Recently Used),最近最少使用的,最长时间不用的对象--> <!--100秒刷新,如果你不配置它,那么当SQL被执行的时候才会去刷新缓存--> <cache eviction="LRU" flushInterval="10000"/>
单个sql可以禁用二级缓存
<select id="getStudentById" parameterType="java.lang.Integer" resultType="Student"
useCache="false">
刷新缓存
<select id="getStudentById" parameterType="java.lang.Integer" resultType="Student"
flushCache="true">
第三方缓存
存入第三方,比如ehcache,Memcached、redis 等
测试ehcache,引依赖
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.1.0</version>
</dependency>
配置mapper
<mapper namespace = “com.xinzhi.entity.User” >
<cache type="org.mybatis.caches.ehcache.EhcacheCache" eviction="LRU"
flushInterval="10000" size="1024" readOnly="true"/>
</mapper>
ehcache的 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<!--
diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下:
user.home – 用户主目录
user.dir – 用户当前工作目录
java.io.tmpdir – 默认临时文件路径
-->
<diskStore path="./tmpdir/Tmp_EhCache"/>
<defaultCache
eternal="false"
maxElementsInMemory="10000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="259200"
memoryStoreEvictionPolicy="LRU"/>
</ehcache>
数据一致性
怎么能保证缓存中的数据就是正确的,在MyBatis中,当mapper中有更新操作时,直接清空缓存。