环境:idea 2018 、Maven
非常感谢B站的楠哥
一、mybatis的作用
- MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。
- MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。
- MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的POJOs(PlainOrdinary JavaObject,普通的 Java对象)映射成数据库中的记录。
二、mybatis的优缺点
sql语句与代码分离,存放于xml配置文件中:
- 优点:便于维护管理,不用在java代码中找这些语句;
- 缺点: JDBC方式可以用打断点的方式调试,但是Mybatis不能,需要通过log4j日志输出日志信息帮助调试,然后在配置文件中修改。
用逻辑标签控制动态SQL的拼接:
- 优点:用标签代替编写逻辑代码;
- 缺点:拼接复杂SQL语句时,没有代码灵活,拼写比较复杂。不要使用变通的手段来应对这种复杂的语句。
查询的结果集与java对象自动映射:
- 优点:保证名称相同,配置好映射关系即可自动映射或者,不配置映射关系,通过配置列名=字段名也可完成自动映射。
- 缺点:对开发人员所写的SQL依赖很强。
编写原生SQL:
- 优点:接近JDBC,比较灵活。
- 缺点:对SQL语句依赖程度很高;并且属于半自动,数据库移植比较麻烦,比如mysql数据库编程Oracle数据库,部分的sql语句需要调整。
最重要的一点,使用的人多!公司需要!但是应为用了反射,效率会下降,所有有些公司会使用原生的jdbc
三、代理模式
mybatis主要使用基于接口的代理模式
- 静态代理
- 动态代理
在mybatis中主要使用动态代理,需要理解代理模式的主要实现方式,但在实际使用中mybatis已经帮封装好了,只需要调用一些方法就行。
代理模式在mybatis中的应用举例:
在测试类中Test:
private SqlSession session;
/**
* 在每个测试方法执行之前执行,获取SQLSession对象
*/
@Before
public void before() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
session = sqlSessionFactory.openSession();
}
/**
* 测试SQL方法
*/
@Test
public void testInsertTeacher(){
//获取代理对象
TeacherMapper mapper = session.getMapper(TeacherMapper.class);
Teacher teacher = new Teacher();
teacher.setTName("王五");
//通过代理对象调用方法
int row = mapper.insertTeacher(teacher);
System.out.println(row);
}
/**
* 在每个测试方法执行完后执行
*/
@After
public void after(){
// 提交
session.commit();
// 清空
session.close();
}
}
四、Maven配置
在pom.xml文件中:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>mybatis</groupId>
<artifactId>mybatis-study</artifactId>
<version>1.0-SNAPSHOT</version>
<!--相关依赖-->
<dependencies>
<dependency>
<!--通用工具包-->
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.4</version>
</dependency>
<!--lombok依赖作用:不需要生成set和get方法-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
</dependency>
<!--mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<!--mysql依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--日志依赖-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--测试依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
五、mybatis核心配置文件
mybatis核心配置文件一般约定名为:mybatis-config.xml
<?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>
<settings>
<!--开启标准日志-->
<!--<setting name="logImpl" value="STDOUT_LOGGING"/>-->
<!--使用log4j打印日志-->
<setting name="logImpl" value="LOG4J"/>
<!--开启驼峰命名自动映射-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!--给包entity下所有的实体类定义一个别名:默认为类名,不区分大小写-->
<typeAliases>
<package name="com.crc.entity" />
</typeAliases>
<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=false;useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!--将接口类的配置文件注射给核心配置文件,这样才能达到对映的联系-->
<mappers>
<mapper resource="mappers/UserMapper.xml" />
<mapper class="com.crc.dao.AdminMapper" />
<mapper resource="mappers/DeptMapper.xml" />
<mapper resource="mappers/EmployeeMapper.xml" />
<mapper resource="mappers/TeacherMapper.xml" />
<mapper resource="mappers/StudentMapper.xml" />
</mappers>
</configuration>
六、mybatis的主要组成部分:
1、实体类:一般写在entity包中,对应数据库(一表一实体类),如:user表(User)。
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
private int id;
private String username;
private String password;
}
2、持久层:一般写在dao包中,对应实体类。通常使用Mapper结尾的接口。如:UserMapper
public interface UserMapper {
//SQL方法,与resource中的对应的配置文件相互对应
List<User> selectUsers();
}
3、配置文件:一般写在resource资源文件中。通常与持久层中的接口名一致。如: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.crc.dao.UserMapper">
<!--接口中的方法名,类型名-->
<select id="selectUsers" resultType="com.crc.entity.User">
<!-- 执行的sql语句-->
select id,username,password from user
</select>
</mapper>
4、测试类:通过获取代理对象调用接口中的方法。(具体实现请看上面的代理模式代码)
总结:
mybatis实现SQL语句过程主要由这几个部分组成:接口类UserMapper编写SQL实现方法(方便java代码调用),配置文件UserMapper.xml 实现具体的接口方法(编写SQL语句)。
重要!!!一定要在mybatis-config.xml核心配置文件中配置对应的XXXMapper.xml 文件
<!--将接口类的配置文件注射给核心配置文件,这样才能达到对映的联系-->
<mappers>
<!--通过配置文件-->
<mapper resource="mappers/UserMapper.xml" />
<!--通过类-->
<mapper class="com.crc.dao.AdminMapper" />
</mappers>
以上是最常用的配置方式。
方法二:
mybatis最初配置信息是基于 XML ,映射语句(SQL)也是定义在 XML 中的。而到MyBatis 3提供了新的基于注解的配置。不幸的是,Java 注解的的表达力和灵活性十分有限。最强大的 MyBatis 映射并不能用注解来构建
sql 类型主要分成 :
@select ()
@update ()
@Insert ()
@delete ()
注意:利用注解开发就不需要mapper.xml映射文件了 .
七、动态SQL
1、简单介绍 < select > < insert > 等标签中节点的作用:
如
<!--
id:对应接口类中的方法名,也是两个文件之间相互映射的关键
resultType:方法的返回值类型,参数为类的全类名或者别名
parameterType:方法的参数类型,参数为类的全类名或者别名
-->
<select id="selectUserById" resultType="com.crc.entity.User" parameterType="int">
select id,username,password from user where id = #{id}
</select>
2、动态SQL中的元素:
元素 | 作用 | 备注 |
---|---|---|
if | 判断语句 | 单条件分支判断 |
choose、when、otherwise | 相当于Java中的 case when语句 | 多条件分支判断 |
trim、where、set | 辅助元素 | 用于处理一些SQL拼装问题 |
foreach | 循环语句 | 在in语句等列举条件常用 |
if元素(最常用)
<select id="findUserById" resultType="com.xinzhi.entity.User">
select id,username,password from user
where 1 =1
<if test="id != null and id != ''">
AND id = #{id}
</if>
</select>
where元素
上面的select语句我们加了一个 1=1 的绝对true的语句,目的是为了防止语句错误,变成 SELECT * FROM
student WHERE 这样where后没有内容的错误语句。这样会有点奇怪,此时可以使用 元素
<select id="findUserById" resultType="com.xinzhi.entity.User">
select id,username,password from user
<where>
<if test="id != null and id != ''">
AND id = #{id}
</if>
</where>
</select>
choose、when、otherwise元素
有些时候我们还需要多种条件的选择,在Java中我们可以使用switch、case、default语句,而在映射器的动态语句中可以使用choose、when、otherwise元素。
<!-- 有name的时候使用name搜索,没有的时候使用id搜索 -->
<select id="select" resultType="com.xinzhi.entity.User">
SELECT * FROM user
WHERE 1=1
<choose>
<when test="name != null and name != ''">
AND username LIKE concat('%', #{username}, '%')
</when>
<when test="id != null">
AND id = #{id}
</when>
</choose>
</select>
trim元素
有时候我们要去掉一些特殊的SQL语法,比如常见的and、or,此时可以使用trim元素。trim元素意味着我们需要去掉一些特殊的字符串,prefix代表的是语句的前缀,而prefixOverrides代表的是你需要去掉的那种字符串,suffix表示语句的后缀,suffixOverrides代表去掉的后缀字符串。
<select id="select" resultType="com.xinzhi.entity.User">
SELECT * FROM user
<trim prefix="WHERE" prefixOverrides="AND">
<if test="username != null and username != ''">
AND username LIKE concat('%', #{username}, '%')
</if>
<if test="id != null">
AND id = #{id}
</if>
</trim>
</select>
set元素
在update语句中,如果我们只想更新某几个字段的值,这个时候可以使用set元素配合if元素来完成。注意:set元素遇到,会自动把,去掉。
<update id="update">
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>
foreach元素
foreach元素是一个循环语句,它的作用是遍历集合,可以支持数组、List、Set接口。
<select id="select" resultType="com.xinzhi.entity.User">
SELECT * FROM user
WHERE id IN
<foreach collection="ids" open="(" close=")" separator="," item="id">
#{id}
</foreach>
</select>
SQL片段
有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。
<!--提取SQL片段-->
<sql id ="select" > select * from </sql>
<!--引用SQL片段-->
<select id="queryBlogIf" parameterType="map" resultType="blog">
<include refid = "select">
</select>
八、#{} 与 ${} 的区别
相同点:
- #{}:可以获取map中的值或者pojo对象属性的值;
- ${}:可以获取map中的值或者pojo对象属性的值;
区别:
- #{}:是以预编译的形式,将参数设置到sql语句中;PreparedStatement;防止sql注入
- ${}:取出的值直接拼装在sql语句中;会有安全问题;
注意:大多情况下,我们去参数的值都应该去使用#{};
九、resultMap
属性名和字段名不一致,我们一般都会按照约定去设计数据的,但确实阻止不了一些孩子,瞎比起名字。
使用结果集映射->resultMap 【推荐】
<resultMap id="UserMap" type="User">
<!-- id为主键 -->
<id column="id" property="id"/>
<!-- column是数据库表的列名 , property是对应实体类的属性名 -->
<result column="username" property="name"/>
<result column="password" property="password"/>
</resultMap>
<select id="selectUserById" resultMap="UserMap">
select id , username , password from user where id = #{id}
</select>
多表维护:
association 元素:
<resultMap id="select-employee-map" type="com.crc.entity.Employee">
<!--
association:复杂对象,用于一对一关系(一个员工对应一个部门)
property:在实体类的属性名
column:在表中对应的列名
javaType:属性的类型
select:将要执行的SQL
根据查询SQL获得的 d_id 的值进行查询select节点
过程理解:通过查询员工表employee 获得字段信息(如:d_id = 2)
继续通过字段信息查询下一条SQL语句,将查到的结果存入实体类中
最后通过字符串拼接
-->
<association property="dept" column="d_id" javaType="com.crc.entity.Dept"
select="com.crc.dao.DeptMapper.getDept" />
</resultMap>
collection 元素:
<resultMap id="select-teacher-map" type="com.crc.entity.Teacher" >
<collection property="student"
javaType="ArrayList" ofType="com.crc.entity.Student"
column="t_id" select="com.crc.dao.StudentMapper.getStuden" />
</resultMap>
十、一些重要的配置
1、驼峰命名法
在数据库中我们通常习惯对表列名使用下划线名字(u_name),而在实体类中我们习惯使用驼峰命名法(uName)。因此会导致数据库列名与实体类属性名不一致的问题,解决方法:在mybatis-config.xml 核心文件中配置如下:
<settings>
<!--开启驼峰命名自动映射-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
2、给自己的类起别名
在xxxMapper.xml 配置文件中,有时方法的返回值类型和参数类型可能使用我们自己定义的类,而每次都使用全类名可能有些复杂,因此可以对类定义别名从而替代全类名
在核心配置文件中加入
<typeAliases>
<!-- 指定单个类起别名-->
<typeAlias type="com.xinzhi.entity.User" alias="user"/>
<!-- 指定某个包内所有类起别名-->
<package name="com.crc.entity"/>
</typeAliases>
< typeAlias > 标签中 有 type 和 alias 两个属性
type 填写 实体类的全类名, alias 可以不填,不填的话,默认是类名,不区分大小写,
alias 填了的话就以 alias里的值为准。
3、日志配置
标准日志实现
指定 MyBatis 应该使用哪个日志记录实现。如果此设置不存在,则会自动发现日志记录实现。
STD:standard out:输出
STDOUT_LOGGING:标准输出日志
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
组合log4j完成日志功能(扩展)
使用步骤:
1、导入log4j的包
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2、配置文件编写 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
3、setting设置日志实现
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
十一、Mybatis缓存
1、为什么要用缓存?
如果缓存中有数据,就不用从数据库获取,大大提高系统性能。
mybatis提供一级缓存和二级缓存
2、一级缓存:(默认开启)
一级缓存是sqlsession级别的缓存
- 在操作数据库时,需要构造sqlsession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据
- 不同的sqlsession之间的缓存区域是互相不影响的。
3、二级缓存:(需要手动开启)
二级缓存是mapper级别的缓存
- 多个sqlsession去操作同一个mapper的sql语句,多个sqlsession可以共用二级缓存,所得到的数据会存在二级缓存区域。
- 二级缓存是跨sqlsession的
- 二级缓存相比一级缓存的范围更大(按namespace来划分),多个sqlsession可以共享一个二级缓存
4、三级缓存
三级缓存是指第三方缓存。
在企业中通常使用第三方缓存。
坑点:
1、注释问题:在配置动态SQL的时候使用idea快捷键注释单行SQL代码时,idea会自动注释成
“-- SQL语句”
虽然显示和正常注释“<!- -->”代码一样效果,但是运行时会报错“Error updating database. Cause: org.apache.ibatis.type.TypeException: Could not set parameters for mapping…”