Mybatis
1. 简介
1. 环境
-
JDK1.8
-
MYSQL5.7
-
maven3.6.3
-
IDEA
2. 回顾
-
JDBC
-
Mysql
-
java
-
Maven
-
Junit
3. 什么是MyBatis
MyBatis:是一款优秀的持久层框架,支持定制化SQL、存储过程和高级映射;避免了JDBC代码和手动设置采纳数以及获取结果集;可以使用简单的XML或注解来配置和映射原生类型、接口和POJO为数据库中的记录
如何获得Mybatis:
-
Maven仓库
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.9</version> </dependency>
-
Github
4. 持久化
数据持久化
-
持久化:程序的数据在持久和瞬时转换的过程
-
内存:断电即失
为什么需要持久化:内存断电即失、内存资源宝贵
5. 持久层
持久层:完成持久化工作的代码块
DAO层,Service层,Controller层
6. 为什么需要Mybatis
-
方便
-
简化了JDBC繁琐的代码,
-
优点:
-
简单易学
-
灵活,不会对应用程序和数据库的现有设计有影响
-
sql写在xml里,便于管理和优化
-
通过提供DAO层,将业务逻辑和数据访问逻辑分离,解除sql与程序代码的耦合,易维护、易单元测试
-
提供xml标签,支持动态编写sql
-
2. 第一个Mybatis程序
思路:搭建环境-->导入Mybatis-->编写代码测试
2.1 搭建环境
-
搭建数据库
-
新建项目
-
新建一个普通的maven项目
-
删除src目录
-
导入maven依赖
-
2.2 创建一个模块
-
编写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> <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/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <mappers> <mapper resource="com/fish/dao/UserMapper.xml"/> </mappers> </configuration>
-
编写Mybatis工具类
static{ try { //使用Mybatis获取sqlSessionFactory对象 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (Exception e) { e.printStackTrace(); } }
2.3 编写代码
-
实体类
-
Dao接口
public interface UserDao { List<User> getUserList(); }
-
接口实现类由原来的UserDaoImpl转变为一个Mapper配置文件
<?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.fish.dao.UserDao"> <!--查询语句--> <select id="getUserList" resultType="com.fish.pojo.User"> select * from mybatis.user </select> </mapper>
3. CRUD
1. namespace
-
namespace中的包名要和Dao/mapper接口的包名一致
2. select
-
id: 对应的namespace中的方法名
-
resultType:Sql语句执行的返回值
-
parameterType:参数类型
3. Insert
-
编写接口
//根据ID查询用户 User getUserById(int id);
-
编写对应的Mapper中的sql语句
<select id="getUserById" parameterType="int" resultType="com.fish.pojo.User"> select * from mybatis.user where id = #{id} </select>
-
测试
@Test public void getUserById(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserDao mapper = sqlSession.getMapper(UserDao.class); User user = mapper.getUserById(1); System.out.println(user); sqlSession.close(); }
4. update
5. delete
6. map
Map传递参数,直接在sql中取出key即可! parameterType="map"
对象传递参数,直接在sql中取出对昂的属性即可! parameterType="Object"
只有一个基本参数类型的情况下,可直接在sql中取到
多个参数用Map或者注解
7. 模糊查询
1. java代码执行的时候,传递通配符 % %
List<User> userList = mapper.getUserLike("%X%");
2. 在sql拼接中使用通配符
select * from mybatis.user where name like "%#{value}%"
4. 配置解析
-
properties:属性
-
settings:设置
-
typeAliases:类型别名
-
typeHandlers:类型处理器
-
objectFactory:对象工厂
-
plugins:插件
-
environments:环境配置
-
mappers:映射器
1. 核心配置文件
mybatis-config.xml
2. 环境配置
enviroments
mybatis可以配置成适应多种环境,但是sqlSessionFactory实例只能选择一种环境
mybatis的默认事务管理器:JDBC
mybatis的连接池:POOLED
3. 属性
properties:通过properties属性可以实现引用配置文件
编写配置文件:
driver=com.fish.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8 username=root password=123456
在核心配置文件中映入:
<properties resource="db.properties"> <property name="username" value="root"/> <property name="password" value="123456"/> </properties>
-
可以直接引入 外部文件
-
可以增加属性配置
-
属性配置和外部文件配置有字段冲突时,优先使用外部文件配置
4. 映射器
mappers:
-
使用相对于类路径的资源引用
MapperRegistry:注册绑定Mapper文件
<mappers> <mapper resource="com/fish/dao/UserMapper.xml"/> </mappers>
-
使用映射器接口实现类的完全限定类名
-
使用class文件绑定注册
5. 生命周期和作用域
生命周期和作用域的错误使用会导致严重的并发问题
sqlSessionFactoryBuilder:
-
一旦创建了sqlSession,就不在需要它了
-
局部变量
sqlSessionFactory:
-
数据库连接池
-
一旦创建在应用的运行期间一直存在
-
最佳作用域:应用作用域
sqlSession:
-
连接到连接池的请求
-
关闭
-
最佳作用域:请求和方法
5. 属性名和字段名不一致
1. 起别名
<select id="getUserById" parameterType="int" resultType="com.fish.pojo.User"> select id,name,pwd as password from mybatis.user where id = #{id} </select>
2. resultMap
结果集映射:
6. 日志
6.1 日志工厂
数据库操作,出现了异常,需要排错,日志就是最好的助手
-
SLF4J
-
LOG4J 【***】
-
LOG4J2
-
JDK_LOGGING
-
COMMONS_LOGGING
-
STOUT_LOGGING 【***】
-
NO_LOGGING
6.1 STOUT_LOGGING
<settings> <setting name="logImpl" value="STOUT_LOGGING"/> </settings>
6.2 LOG4J
<settings> <setting name="logImpl" value="LOG4J"/> </settings>
### 配置根 ### log4j.rootLogger = debug,console ,fileAppender,dailyRollingFile,ROLLING_FILE,MAIL,DATABASE ### 设置输出sql的级别,其中logger后面的内容全部为jar包中所包含的包名 ### log4j.logger.org.apache=dubug log4j.logger.java.sql.Connection=dubug log4j.logger.java.sql.Statement=dubug log4j.logger.java.sql.PreparedStatement=dubug log4j.logger.java.sql.ResultSet=dubug ### 配置输出到控制台 ### log4j.appender.console = org.apache.log4j.ConsoleAppender log4j.appender.console.Target = System.out log4j.appender.console.layout = org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern = %d{ABSOLUTE} %5p %c{ 1 }:%L - %m%n ### 配置输出到文件 ### log4j.appender.fileAppender = org.apache.log4j.FileAppender log4j.appender.fileAppender.File = logs/log.log log4j.appender.fileAppender.Append = true log4j.appender.fileAppender.Threshold = DEBUG log4j.appender.fileAppender.layout = org.apache.log4j.PatternLayout log4j.appender.fileAppender.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n ### 配置输出到文件,并且每天都创建一个文件 ### log4j.appender.dailyRollingFile = org.apache.log4j.DailyRollingFileAppender log4j.appender.dailyRollingFile.File = logs/log.log log4j.appender.dailyRollingFile.Append = true log4j.appender.dailyRollingFile.Threshold = DEBUG log4j.appender.dailyRollingFile.layout = org.apache.log4j.PatternLayout log4j.appender.dailyRollingFile.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n ### 配置输出到文件,且大小到达指定尺寸的时候产生一个新的文件 ### log4j.appender.ROLLING_FILE=org.apache.log4j.RollingFileAppender log4j.appender.ROLLING_FILE.Threshold=ERROR log4j.appender.ROLLING_FILE.File=rolling.log log4j.appender.ROLLING_FILE.Append=true log4j.appender.ROLLING_FILE.MaxFileSize=10KB log4j.appender.ROLLING_FILE.MaxBackupIndex=1 log4j.appender.ROLLING_FILE.layout=org.apache.log4j.PatternLayout log4j.appender.ROLLING_FILE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n ### 配置输出到邮件 ### log4j.appender.MAIL=org.apache.log4j.net.SMTPAppender log4j.appender.MAIL.Threshold=FATAL log4j.appender.MAIL.BufferSize=10 log4j.appender.MAIL.From=chenyl@yeqiangwei.com log4j.appender.MAIL.SMTPHost=mail.hollycrm.com log4j.appender.MAIL.Subject=Log4J Message log4j.appender.MAIL.To=chenyl@yeqiangwei.com log4j.appender.MAIL.layout=org.apache.log4j.PatternLayout log4j.appender.MAIL.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n ### 配置输出到数据库 ### log4j.appender.DATABASE=org.apache.log4j.jdbc.JDBCAppender log4j.appender.DATABASE.URL=jdbc:mysql://localhost:3306/test log4j.appender.DATABASE.driver=com.mysql.jdbc.Driver log4j.appender.DATABASE.user=root log4j.appender.DATABASE.password= log4j.appender.DATABASE.sql=INSERT INTO LOG4J (Message) VALUES ('[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n') log4j.appender.DATABASE.layout=org.apache.log4j.PatternLayout log4j.appender.DATABASE.layout.ConversionPattern=[framework] %d - %c -%-4r [%t] %-5p %c %x - %m%n log4j.appender.A1=org.apache.log4j.DailyRollingFileAppender log4j.appender.A1.File=SampleMessages.log4j log4j.appender.A1.DatePattern=yyyyMMdd-HH'.log4j' log4j.appender.A1.layout=org.apache.log4j.xml.XMLLayout
7. 分页
问什么要分页:减少数据的处理量
使用limit分页:
select * from user limit x,x
使用mybatis实现分页:
-
接口
//分页 List<User> getUserByLimit(Map<String,Integer> map);
-
Mapper.xml
<select id="getUserByLimit" parameterType="map" resultType="user"> select * from mybatis.user limit #{startIndex},#{pageSize} </select>
8. 使用注解开发
8.1 面向接口编程
原因:解耦;可扩展;提高复用;分层开发中,上层不用管下层具体的实现
-
对于接口的理解:接口是定义(规范,约束)与实现的分离
-
接口本身反映了开发人员对系统的抽象理解
-
接口有两类:对个体的抽象,即抽象体;对个体某一方面的抽象,即抽象面
三个面向的区别:
-
面向对象:考虑问题时,以对象为单位,考虑它的属性和方法
-
面向过程:考虑问题时,以具体的流程为单位,考虑它的实现
-
面向接口:针对复用技术,体现对系统整体的架构
8.2 使用注解开发
@param()注解:
-
基本类型和String类型需要添加,引用类型不需要加
-
只有一个基本类型可以忽略
-
SQL中引用的是@param()设定的属性名
9. Lombok
1. 使用步骤
-
在idea中安装Lombok插件
-
在项目中导入Lombok的jar包
-
注解
@Getter and @Setter @FieldNameConstants @ToString @EqualsAndHashCode @AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor @Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog @Data @Builder @SuperBuilder @Singular @Delegate @Value @Accessors @Wither @With @SneakyThrows @val @var experimental @var @UtilityClass Lombok config system
@Data:无参构造、get、set、toString、hashCode、equals
10. 多对一处理
按照查询嵌套处理:
<mapper namespace="com.fish.dao.StudentMapper"> <select id="getStudent" resultMap="StudentTeacher"> select * from student </select> <resultMap id="StudentTeacher" type="Student"> <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/> </resultMap> <select id="getTeacher" resultType="teacher"> select * from teacher where id = #{id} </select> </mapper>
按照结果嵌套处理
<select id="getStudent2" resultMap="StudentTeacher2"> select s.id sid,s.name sname,t.name tname from student s,teacher t where s.tid=t.id; </select> <resultMap id="StudentTeacher2" type="Student"> <result property="id" column="sid"/> <result property="name" column="sname"/> <association property="teacher" javaType="Teacher"> <result property="name" column="tname"/> </association> </resultMap>
多对一查询方式:
-
子查询
-
连表查询
11. 一对多处理
12. 动态SQL
动态SQL:根据不同的条件生成不同的SQL语句
搭建环境:
CREATE TABLE `blog` ( `id` VARCHAR(50) NOT NULL COMMENT '博客id', `title` VARCHAR(100) NOT NULL COMMENT '博客标题', `author` VARCHAR(30) NOT NULL COMMENT '博客作者', `create_time` DATETIME NOT NULL COMMENT '创建时间', `views` INT(30) NOT NULL COMMENT '浏览量' )ENGINE INNODB DEFAULT CHARSET=utf8
-
导包
-
编写配置文件
-
编写实体类
@Data public class blog { private int id; private String title; private String author; private Date createTime; private int views; }
-
编写实体类Mapper接口和Mapper.xml文件
IF
<select id="queryBlogIF" parameterType="map" resultType="blog"> select * from mybatis.blog where 1=1 <if test="title != null"> and title = #{title} </if> <if test="author != null"> and author = #{author} </if> </select>
choose:when,otherwise
trim:where,set
<select id="queryBlogIF" parameterType="map" resultType="blog"> select * from mybatis.blog <where> <if test="title != null"> and title = #{title} </if> <if test="author != null"> and author = #{author} </if> </where> </select>
动态SQL本质还是SQL语句,只是可以在SQL层面去执行一个逻辑代码
SQL片段
抽取部分功能,方便复用
-
使用SQL标签抽取公共部分
-
在需要使用的地方使用include标签引用
Foreach
select * from user where 1=1 and <foreach item="id" collection="ids" open="(" separator="or" close=")"> #{id} </foreach> (id=1 or id=2 or id=3)
面试高频:
-
Mysql引擎
-
InnoDB底层原理
-
索引
-
索引优化
13. 缓存
1. 什么是缓存
-
存储在内存中的临时数据
-
提高查询效率,解决了高并发系统的性能问题
2. 为什么使用缓存
-
减少和数据库的交互次数
-
减少系统开销
-
替考系统效率
3. 什么情况下使用缓存
经常查询切不经常改变的数据
4. Mybatis缓存
mybatis系统中默认定义了两极缓存:一级缓存和二级缓存
-
默认情况下,只有一级缓存开启(SQLSession级别的缓存,也成为本地缓存)
-
二级缓存需要手动开启和配置,是基于namespace级别的缓存
-
为了提高扩展性,mybatis定义了缓存接口cache,通过实现cache接口来自定义二级缓存
5.一级缓存
一级缓存也叫本地缓存
-
与数据库同一次会话中查询到的数据会放在本地缓存中
-
以后如果需要获取相同的数据,直接从缓存中拿
-
开启全局缓存
<setting name="cacheEnabled" value="true"/>
-
开启二级缓存
<cache/>
小结:
-
只要开启了二级缓存,在同一个Mapper下就有效
-
所有数据都会先放在一级缓存中
-
只有当会话关闭的时候才会提交到二级缓存
缓存顺序:
-
先在二级缓存中查找
-
再看一级缓存
-
查询数据库