MyBatis
简介
什么是 MyBatis?
- MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。
- MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
- MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
相关资源
- 文档:https://mybatis.org/mybatis-3/zh/index.html
- 下载地址:https://github.com/mybatis/mybatis-3/releases
- maven仓库
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>
持久化
- 数据持久化:持久化就是将程序的数据在持久状态和瞬时状态转化的过程
- 数据库(jdbc),io文件持久化
持久层
- 完成持久化工作的代码块
- 层界限十分明显
为什么需要mybatis
- 传统的JDBC代码太复杂了需要简化
- 帮助程序员将数据存入到数据库中
- 不用mybatis也可以,但是用了更容易上手。技术没有高低之分
- 优点:
- 简单易学
- 灵活
- sql和代码分离 提高可维护性
- 提供映射标签,支持对象与数据库orm字段关系映射
- 提供对象关系映射标签,支持对象关系组维护
- 提供xml标签,支持编写动态sql
- 生态好
第一个MyBatis程序
思路->搭建环境->导入MyBatis->编写代码->测试!
搭建环境
搭建数据库
CREATE DATABASE `mybatis`;
use `mybatis`;
CREATE TABLE `user`(
`id` INT (20) not null PRIMARY key,
`name` VARCHAR(30) DEFAULT NULL,
`pwd` VARCHAR(30) DEFAULT NULL
)ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO `user` (`id`,`name`,`pwd`) VALUES
(1,'林青霞','123456'),
(2,'樱木花道','123456'),
(3,'孙悟空','123456')
新建项目
- 新建普通maven项目
- 删除src目录
- 导入依赖(mysql-connctor-java mybatis junit)
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.15</version>
</dependency>
</dependencies>
创建子模块
- 编写mybatis核心配置文件(resources)
<?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.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="289989142"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/lhy/dao/userMapper.xml"/>
</mappers>
</configuration>
- 编写mybatis工具类(utils)
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static{
try {
//获取SqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//返回SqlSession对象
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
编写代码
实体类
public class user {
//实体类
private int id;
private String name;
private String pwd;
public user() {
}
public user(int id, String name, String pwd) {
this.id = id;
this.name = name;
this.pwd = pwd;
}
@Override
public String toString() {
return "user{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
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 String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
}
Dao接口
public interface userDao {
List<user> getUserList();
}
接口实现类
<?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接口-->
<mapper namespace="com.lhy.dao.userDao" >
<select id="getUserList" resultType="com.lhy.pojo.user">
select * from mybatis.user
</select>
</mapper>
测试
- junit
public class userDaoTest {
@Test
public void test(){
//通过自己写的工具类获取sqlSession
SqlSession sqlSession = MybatisUtils.getSqlSession();
//执行sql方式1 getMapper
userDao mapper = sqlSession.getMapper(userDao.class);
List<user> userList = mapper.getUserList();
for (user user : userList) {
System.out.println(user);
}
//关闭sqlSession
sqlSession.close();
}
}
CRUD
- select 查询语句
- id:对应的namespace中的方法名
- resultType: sql语句的返回值
- parameterType :参数类型
1.编写接口或方法
//查询全部用户
List<user> getUserList();
2.编写对应的mapper中的sal语句
<select id="getUserList" resultType="com.lhy.pojo.user">
select * from mybatis.user
</select>
3.测试
@Test
public void test(){
//通过自己写的工具类获取sqlSession
SqlSession sqlSession = MybatisUtils.getSqlSession();
//执行sql方式1 getMapper
userMapper mapper = sqlSession.getMapper(userMapper.class);
List<user> userList = mapper.getUserList();
for (user user : userList) {
System.out.println(user);
}
//关闭sqlSession
sqlSession.close();
}
- insert
<insert id="insertUser" parameterType="com.lhy.pojo.user" >
insert into mybatis.user (id, name, pwd) values (#{id},#{name},#{pwd});
</insert>
- update
<update id="updateUser" parameterType="com.lhy.pojo.user">
update mybatis.user set name = #{name} ,pwd = #{pwd} where id=#{id};
</update>
- delete
<delete id="deleteUser" parameterType="int" >
delete from mybatis.user where id=#{id};
</delete>
注意:增删改需要提交事务!!
万能的Map
假设我们的实体类或者数据库中的表,字段或者参数过多,我们应该考虑使用map
Map传递参数,直接在sql中取出key即可
<insert id="insertUser2" parameterType="map">
insert into mybatis.user (id, name, pwd ) values (#{userid},#{username},#{userpwd});
</insert>
@Test
public void insertUser2(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
userMapper mapper = sqlSession.getMapper(userMapper.class);
Map<String, Object> map = new HashMap<String, Object>();
map.put("userid",5);
map.put("username","map");
map.put("userpwd",5);
System.out.println(mapper.insertUser2(map));
sqlSession.commit();
sqlSession.close();
}
模糊查询
<select id="getUserLike" resultType="com.lhy.pojo.user">
select * from mybatis.user where name like #{value}
</select>
@Test
public void likeSelect(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
userMapper mapper = sqlSession.getMapper(userMapper.class);
List<user> userList = mapper.getUserLike("%李%");
for (user user : userList) {
System.out.println(user);
}
sqlSession.close();
}
}
配置解析
核心配置文件
- mybatis-config.xml
environments环境变量
- Mybatis可以配置多套环境,每次只能选择一套
- Mybatis默认的事务管理器就是JDBC,连接池:POOLED
properties 属性
我们可以通过properties 属性来实现引用配置文件
这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。【db.properties】
编写一个配置文件
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8
username=root
password=289989142
在核心配置文件中引入
<properties resource="db.properties"></properties>
可以直接引入外部文件
可以在其中增加一些属性配置
如果两处配置有同一字段 优先使用外部配置文件的!
类型别名(typeAliases)
类型别名可为 Java 类型设置一个缩写名字。
它仅用于 XML 配置,意在降低冗余的全限定类名书写。
方法一,为某个指定类起别名
<typeAliases>
<typeAlias type="com.lhy.pojo.user" alias="user"></typeAlias>
</typeAliases>
方法二,扫描一个包为其中的实体类起别名,默认为这个类的类名首字母小写(可以通过注解修改 在实体类上加@Alias)
<typeAliases>
<package name="com.lhy.pojo"/>
</typeAliases>
在实体类比较少的时候使用第一种,比较多的时候可以用第二种
settings 设置
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 下表描述了设置中各项设置的含义、默认值等。
自动转换驼峰和下划线
日志
mapper 映射器
MapperRegistry 注册绑定我们的Mapper文件
方式一:
<mappers>
<mapper resource="com/lhy/dao/userMapper.xml"/>
</mappers>
方式二:使用class文件绑定注册
注意 接口和它的mapper配置文件必须同名 并且必须在同一个包里
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
<mapper class="com.lhy.dao.userMapper"/>
</mappers>
方式三:
使用扫描包注册绑定 注意点与方式二一样
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
<package name="com.lhy.dao"/>
</mappers>
生命周期和作用域
作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题。
- SqlSessionFactoryBuilder 一旦创建了 SqlSessionFactory,就不再需要它了 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)
- SqlSessionFactory 可以想象成数据库连接池 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。因此 SqlSessionFactory 的最佳作用域是应用作用域最简单的就是使用单例模式或者静态单例模式。
- SqlSession 可以想象成一个请求 每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 用完后需要赶紧关闭 否则资源被占用
这里的每一个mapper就代表一个具体的业务
解决属性名和字段名不一致的问题 ResultMap
问题
数据库中的字段
新建一个项目,拷贝之前的,测试实体类字段不一致的情况
public class user {
//实体类
private int id;
private String name;
private String password;
测试出现错误
解决方法:
- 起别名
select id,name,pwd as password form mybatis.user where id = #{id}
ResultMap
结果集映射
id name pwd
id name password
column为数据库中的字段
property为实体类中的属性
<resultMap id="userMap" type="user">
<result column="pwd" property="password"/>
</resultMap>
<select id="getUserById" parameterType="int" resultMap="userMap">
select * from mybatis.user where id=#{id}
</select>
resultMap 元素是 MyBatis 中最重要最强大的元素。
ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。
日志
日志工厂
如果一个数据库操作出现了异常需要排错,日志就是最好的帮手
- SLF4J
- LOG4J【掌握】
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING【掌握】
- NO_LOGGING
在mybatis中具体使用哪个根据设置来定setting
STDOUT_LOGGING标准日志输出
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
Opening JDBC Connection
Created connection 690339675.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@2925bf5b]
==> Preparing: select * from mybatis.user where id=?
==> Parameters: 3(Integer)
<== Columns: id, name, pwd
<== Row: 3, 孙悟空, 123456
<== Total: 1
user{id=3, name='孙悟空', password='123456'}
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@2925bf5b]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@2925bf5b]
Returned connection 690339675 to pool.
Process finished with exit code 0
LOG4J
什么是log4j?
1.Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件
2.我们也可以控制每一条日志的输出格式
3.通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。
4.最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
1.先导包
<!-- https://mvnrepository.com/artifact/log4j/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/lhy.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.配置log4j为日志的实现
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
4.测试运行
??org.apache.ibatis.logging.LogFactory??-Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
??org.apache.ibatis.logging.LogFactory??-Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
??org.apache.ibatis.datasource.pooled.PooledDataSource??-PooledDataSource forcefully closed/removed all connections.
??org.apache.ibatis.datasource.pooled.PooledDataSource??-PooledDataSource forcefully closed/removed all connections.
??org.apache.ibatis.datasource.pooled.PooledDataSource??-PooledDataSource forcefully closed/removed all connections.
??org.apache.ibatis.datasource.pooled.PooledDataSource??-PooledDataSource forcefully closed/removed all connections.
??org.apache.ibatis.transaction.jdbc.JdbcTransaction??-Opening JDBC Connection
??org.apache.ibatis.datasource.pooled.PooledDataSource??-Created connection 681094281.
??org.apache.ibatis.transaction.jdbc.JdbcTransaction??-Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@2898ac89]
??com.lhy.dao.userMapper.getUserById??-==> Preparing: select * from mybatis.user where id=?
??com.lhy.dao.userMapper.getUserById??-==> Parameters: 3(Integer)
??com.lhy.dao.userMapper.getUserById??-<== Total: 1
user{id=3, name='孙悟空', password='123456'}
??org.apache.ibatis.transaction.jdbc.JdbcTransaction??-Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@2898ac89]
??org.apache.ibatis.transaction.jdbc.JdbcTransaction??-Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@2898ac89]
??org.apache.ibatis.datasource.pooled.PooledDataSource??-Returned connection 681094281 to pool.
Process finished with exit code 0
简单使用
1.在要使用log4j的类中导入包(apache的)
import org.apache.log4j.Logger;
2.日志对象,加载参数为当前类的class
static Logger logger = Logger.getLogger(userMapperTest.class);
3.日志级别
logger.info("info:进入了log4jTest");
logger.debug("debug:进入了debug");
logger.error("error:error");
分页
- 减少数据处理量
使用limit分页
SELECT * from user limit startIndex,pageSize
SELECT * from user limit 0,2 (从0开始 查询两个)
- 使用mybatis分页
- 接口
List<user> getUserByLimit (Map<String,Integer> map);
* mapper.xml
<select id="getUserByLimit" parameterType="map" resultMap="userMap">
select * from mybatis.user limit #{startIndex},#{pageSize}
</select>
* 测试
@Test
public void getUserByLimitTest(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
userMapper mapper = sqlSession.getMapper(userMapper.class);
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("startIndex",0);
map.put("pageSize",2);
List<user> list = mapper.getUserByLimit(map);
for (user user : list) {
System.out.println(user);
}
sqlSession.close();
}
使用注解开发
面向接口编程
根本原因 解耦
使用注解开发(简单的sql可以用)
接口
@Select("select * from user")
List<user> getUsers();
核心配置文件
<mappers>
<mapper class="com.lhy.dao.userMapper"/>
</mappers>
本质:反射机制实现
底层:动态代理
关于@Param()注解
- 基本类型的参数或者String类型需要加上
- 引用了类型不需要加
- 如果只有一个基本类型的话可以忽略 但建议加上
- 我们在sql中引用的就是这里@Param中设定的属性名
- #{} 防止sql注入 ${}
Mybatis执行流程
Lombok
1.在IDEA中安装插件
2.导入依赖
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>provided</scope>
</dependency>
3.使用注解偷懒
@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
@AllArgsConstructor 有参构造
@NoArgsConstructor 无参构造
多对一处理
- 多个学生关联一个老师 关联
按照查询嵌套处理(子查询)
按照结果嵌套处理
一对多处理
- 一个老师有多个学生 集合
按照结果嵌套处理
按照查询嵌套处理
注意: - 保证sql的可读性
- 注意一对多和多对一中属性名和字段的问题
- 如果问题不好排查错误,可以使用日志,log4j
- 面试:Mysql引擎,innoDB底层原理,索引,索引优化
动态SQL
动态sql就是指根据不同的条件生成不同得sql语句
缓存
放在内存中的临时数据
将用户经常查询的数据放在缓存中
- 经常查询且不经常改变的数据适用于缓存
一级缓存
在一个sqlSession中有效
sqlSession自带一级缓存
二级缓存
在一个mapper中有效(namespace)
这样开启