一、介绍
- MyBatis是一流的持久性框架
- 支持自定义SQL,存储过程和高级映射
- MyBatis消除了几乎所有的JDBC代码以及参数的手动设置和结果检索。MyBatis可以使用简单的XML或注释进行配置,并将图元,映射接口和Java POJO(普通的旧Java对象)映射到数据库记录。
(连接池是创建和管理一个连接的缓冲池的技术,这些连接准备好被任何需要它们的线程使用。)
二、第一个Mybatis程序
2.1 搭建环境
<!--导入依赖 JDBC Mybatis Junit-->
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
2.2 创建模块
- 编写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>
<properties resource="db.properties">
<property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/> <!-- 启用默认值特性 -->
</properties>
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
<typeAliases>
<typeAlias type="com.hz.pojo.User" alias="User"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<package name="com.hz.dao"/>
</mappers>
</configuration>
设置默认值
<dataSource type="POOLED">
<!-- ... -->
<property name="username" value="${username:ut_user}"/> <!-- 如果属性 'username' 没有被配置,'username' 属性的值将为 'ut_user' -->
</dataSource>
修改默认值符号
<property name="org.apache.ibatis.parsing.PropertyParser.default-value-separator" value="?:"/>
<!--修改默认值的分隔符 -->
<dataSource type="POOLED">
..
<property name="username" value="${db:username?:ut_user}"/>
</dataSource>
数据源(dataSource)
dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。
- 大多数 MyBatis 应用程序会按示例中的例子来配置数据源。虽然数据源配置是可选的,但如果要启用延迟加载特性,就必须配置数据源。
有三种内建的数据源类型(也就是 type=“[UNPOOLED|POOLED|JNDI]”):
UNPOOLED– 这个数据源的实现会每次请求时打开和关闭连接。虽然有点慢,但对那些数据库连接可用性要求不高的简单应用程序来说,是一个很好的选择。 性能表现则依赖于使用的数据库,对某些数据库来说,使用连接池并不重要,这个配置就很适合这种情形。UNPOOLED 类型的数据源仅仅需要配置以下 5 种属性:
driver
– 这是 JDBC 驱动的 Java 类全限定名(并不是 JDBC 驱动中可能包含的数据源类)。url
– 这是数据库的 JDBC URL 地址。username
– 登录数据库的用户名。password
– 登录数据库的密码。defaultTransactionIsolationLevel
– 默认的连接事务隔离级别。defaultNetworkTimeout
– 等待数据库操作完成的默认网络超时时间(单位:毫秒)。查看java.sql.Connection#setNetworkTimeout()
的 API 文档以获取更多信息。
作为可选项,你也可以传递属性给数据库驱动。只需在属性名加上“driver.”前缀即可,例如:
driver.encoding=UTF8
这将通过 DriverManager.getConnection(url, driverProperties) 方法传递值为 UTF8
的 encoding
属性给数据库驱动。
POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,能使并发 Web 应用快速响应请求。
除了上述提到 UNPOOLED 下的属性外,还有更多属性用来配置 POOLED 的数据源:
poolMaximumActiveConnections
– 在任意时间可存在的活动(正在使用)连接数量,默认值:10poolMaximumIdleConnections
– 任意时间可能存在的空闲连接数。poolMaximumCheckoutTime
– 在被强制返回之前,池中连接被检出(checked out)时间,默认值:20000 毫秒(即 20 秒)poolTimeToWait
– 这是一个底层设置,如果获取连接花费了相当长的时间,连接池会打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直失败且不打印日志),默认值:20000 毫秒(即 20 秒)。poolMaximumLocalBadConnectionTolerance
– 这是一个关于坏连接容忍度的底层设置, 作用于每一个尝试从缓存池获取连接的线程。 如果这个线程获取到的是一个坏的连接,那么这个数据源允许这个线程尝试重新获取一个新的连接,但是这个重新尝试的次数不应该超过poolMaximumIdleConnections
与poolMaximumLocalBadConnectionTolerance
之和。 默认值:3(新增于 3.4.5)poolPingQuery
– 发送到数据库的侦测查询,用来检验连接是否正常工作并准备接受请求。默认是“NO PING QUERY SET”,这会导致多数数据库驱动出错时返回恰当的错误消息。poolPingEnabled
– 是否启用侦测查询。若开启,需要设置poolPingQuery
属性为一个可执行的 SQL 语句(最好是一个速度非常快的 SQL 语句),默认值:false。poolPingConnectionsNotUsedFor
– 配置 poolPingQuery 的频率。可以被设置为和数据库连接超时时间一样,来避免不必要的侦测,默认值:0(即所有连接每一时刻都被侦测 — 当然仅当 poolPingEnabled 为 true 时适用)。
JNDI – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。这种数据源配置只需要两个属性:
initial_context
– 这个属性用来在 InitialContext 中寻找上下文(即,initialContext.lookup(initial_context))。这是个可选属性,如果忽略,那么将会直接从 InitialContext 中寻找 data_source 属性。data_source
– 这是引用数据源实例位置的上下文路径。提供了 initial_context 配置时会在其返回的上下文中进行查找,没有提供时则直接在 InitialContext 中查找。
和其他数据源配置类似,可以通过添加前缀“env.”直接把属性传递给 InitialContext。比如:
env.encoding=UTF8
这就会在 InitialContext 实例化时往它的构造方法传递值为 UTF8
的 encoding
属性。
你可以通过实现接口 org.apache.ibatis.datasource.DataSourceFactory
来使用第三方数据源实现:
public interface DataSourceFactory {
void setProperties(Properties props);
DataSource getDataSource();
}
org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory
可被用作父类来构建新的数据源适配器,比如下面这段插入 C3P0 数据源所必需的代码:
import org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class C3P0DataSourceFactory extends UnpooledDataSourceFactory {
public C3P0DataSourceFactory() {
this.dataSource = new ComboPooledDataSource();
}
}
为了令其工作,记得在配置文件中为每个希望 MyBatis 调用的 setter 方法增加对应的属性。 下面是一个可以连接至 PostgreSQL 数据库的例子:
<dataSource type="org.myproject.C3P0DataSourceFactory">
<property name="driver" value="org.postgresql.Driver"/>
<property name="url" value="jdbc:postgresql:mydb"/>
<property name="username" value="postgres"/>
<property name="password" value="root"/>
</dataSource>
使用 Spring + MyBatis,则没有必要配置事务管理器,因为 Spring 模块会使用自带的管理器来覆盖前面的配置。
这两种事务管理器类型都不需要设置任何属性。它们其实是类型别名,换句话说,你可以用 TransactionFactory 接口实现类的全限定名或类型别名代替它们。
public interface TransactionFactory {
default void setProperties(Properties props) { // 从 3.5.2 开始,该方法为默认方法
// 空实现
}
Transaction newTransaction(Connection conn);
Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);
}
2.3 编写代码User类
package com.hz.pojo;
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;
}
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接口
```java
public interface UserDao {
public 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.hz.dao.UserDao"> <select id="getUserList" resultType="com.hz.pojo.User"> select * from mybatis.User </select> </mapper>
三、CRUD
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z9ilAcIZ-1626338084938)(mybatis.assets/微信图片_20210617190434.png)]
1.namespace
namespace中的包名与Dao/mapper接口的类名一致
<mapper namespace="com.hz.dao.UserDao">
2.select/insert/update/delete
选择,查询语句
- id:namespace中的方法名
- resultType:sql语句执行的返回值
- paramterType:参数类型
<mapper namespace="com.hz.dao.UserDao">
<select id="getUserList" resultType="com.hz.pojo.User">
select * from mybatis.User
</select>
<!-- 对象中的属性可以直接取出-->
<insert id="addUser" parameterType="com.hz.pojo.User">
insert mybatis.user (id,name,pwd) values (#{id},#{name},#{pwd})
</insert>
<update id="updateUser" parameterType="com.hz.pojo.User">
update mybatis.user set name=#{name},pwd=#{pwd} where id=#{id}
</update>
<delete id="deleteUser" parameterType="com.hz.pojo.User">
delete from mybatis.user where id=#{id};
</delete>
</mapper>
自动生成主键
<insert id="insertAuthor" useGeneratedKeys="true"
keyProperty="id">
insert into Author (username,password,email,bio)
values (#{username},#{password},#{email},#{bio})
</insert>
3.map
-
Dao层
User searchUser(Map<String,Object> map);
-
Mapper.xml
<select id="searchUser" parameterType="com.hz.pojo.User"> select * from user where id = #{ID} and name = #{Name}; </select>
四、配置解析
1.核心配置文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mosBMNio-1626338084945)(mybatis.assets/image-20210317212202311.png)]
- mapper-config.xml
- configuaration(配置)
- peoperties(属性)
- setting(设置)
- typeAliases(类型别名)
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- environments(环境配置)
- environment(环境变量)
- transactionManger(事务管理器)
- dataSource(数据源)
- environment(环境变量)
- databserIdProvider(数据库厂商标识)
- mappers(映射器)
2.环境配置(environments)
Mybatis默认事务管理器:JDBC,连接池:POOLED
3.属性(properties)
两种设置属性的方式(1.建立对应peoperties文件;2.xml文件中设置peoperties)
<properties resource="db.properties"/>
<properties resource="temp.properties">
<property name="name" value="name"/>
</properties>
4.类别名(typeAliases)
<typeAliases>
<typeAlias type="com.hz.pojo.User" alias="User">
</typeAliases>
5.映射器(Mapper)
三种mapper注册方式:
-
xml文件注册
<mappers> <mapper resource="com/hz/dao/UserMapper.xml"/> </mappers>
-
接口注册
<mappers> <mapper class="com.hz.dao.UserMapper"/> </mappers>
注意:UserMapper需要和对应xml文件在同一包中
-
包注册
<mappers> <package name="com.hz.dao"/> </mappers>
五、解决属性名和字段名不一致的问题
1.问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZqJvMoHi-1626338084951)(mybatis.assets/image-20210318205140338.png)]
原因:在pojo包的User类中的属性名为password,而其对应的数据库字段名为pwd,在xml文件中的语句为select * from user
与User类对象,运行时语句等同于select id,name,password from user
2.resultMap
结果集映射(如下 paword->pwd)
属性名 | 字段名 |
---|---|
id | id |
name | name |
password | pwd |
使用resultMap解决映射不一致问题
<resultMap id="UserMap" type="User">
<result column="id" property="id"/>
<result column="name" property="name"/>
<result column="pwd" property="password"/>
</resultMap>
六、日志
1. 日志工厂
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
2. Log4j
-
导入Log4j的包(
org.apache.log4j.Logger
)<!-- https://mvnrepository.com/artifact/log4j/log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
-
Log4j配置文件示例
#将等级为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
Log4j的使用
-
导包(
org.apache.log4j.Logger
) -
实例化logger对象
Logger logger = Logger.getLogger(UserDaoTest.class);
-
日志等级
logger.info("info:"); logger.debug("debug:"); logger.error("error:");
结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5GYcidXF-1626338084960)(mybatis.assets/image-20210319193131873.png)]
七、分页
1. Limit分页
2.RowBound分页
-
接口
List<User> getUserList();
-
xml
<select id="getUserList" resultMap="UserMap"> select * from mybatis.User </select>
-
Test
import org.apache.ibatis.session.RowBounds; public void tese(){ RowBounds rowBounds = new RowBounds(1,2); List<User> userList = sqlSession.selectList("com.hz.dao.UserMapper.getUserList",null,rowBounds); }
3. 使用插件
八、使用注解开发
(简单的sql语句可用注解,较复杂的用xml)
@Select("select * from user")
List<User> getUserList();
@Select("select * from user where if = #{id}")
User getUserById(@Param("id") int id);
@Insert("insert into user(id,name,pwd) values (#{id},#{name},#{password})")
int addUser(User user);
@Delete("delete * from user where id = #{id} ")
int deleteUser(User user);
九、Lombok(插件,不建议使用)
十、联表查询
表如下:
student表
id | name | tid |
---|---|---|
1 | student1 | 1 |
2 | student2 | 1 |
3 | student3 | 1 |
teacher表
id | name |
---|---|
1 | teacher1 |
1、多对一
-
方法一:
select s.id,s.name,t.name from student s,teacher t where s.tid=t.id
;<select id="getStudent" resultMap="StudentTeacher"> select s.id,s.name,t.name tname from student s,teacher t where s.tid=t.i </select> <resultMap id="StudentTeacher" type="Student"> <result property="id" column="id"/> <result property="name" column="name"/> <association property="teacher" javaType="Teacher"> <result property="name" column="tname"> </association> </resultMap>
-
方法二:
<!--javaType为获取的对象--> <select id="getUserList" resultMap="UserMap"> select * from User </select> <resultMap id="StudentTeacher" type="StudentTeacher"> <result property="id" column="id"/> <result column="name" property="name"/> <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/> </resultMap> <select id="getTeacher" resultType="Teacher"> select * from teacher where id = #{id} </select>
2、一对多
-
<select id="getTeacher" resultMap="TeacherStudent"> select s.id sid, s.name sname, t.name tname, t.id tid from student s,teacher t where s.tid = t.id and t.id = #{t id}; </select> <resultMap id="TeacherStudent" type="Teacher"> <result property="id" column="tid"/> <result property="name" column="tname"/> <!-- javatype="" 指定属性类型 集合中的泛型信息用ofType获取 --> <collection property="students" ofType="Student"> <result property="id" column="sid"/> <result column="sname" property="name"/> <result property="tid" column="tid"/> </collection>
小结
javaType & ofType
- javaType 用来指定实体类中的属性
- ofType 用来指定映射到List或者集合中的pojo类型,泛型中的约束类型
十一、动态SQL
https://mybatis.org/mybatis-3/zh/dynamic-sql.html
搭建环境
REATE 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;
创建基础工程
- 导包
- 编写配置文件
- 编写实体类
- 编写实体类对应的Mapper接口和Mapper.xml文件
if
<select id="queryBlog" parameterType="map" resultType="Blog">
select * from blog where title = #{title} and author = #{author}
where 1=1
<if test="title != null">
and title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</select>
choose、when、otherwise
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>
trim、where、set
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</where>
</select>
<update id="updateAuthorIfNecessary">
update Author
<set>
<if test="username != null">username=#{username},</if>
<if test="password != null">password=#{password},</if>
<if test="email != null">email=#{email},</if>
<if test="bio != null">bio=#{bio}</if>
</set>
where id=#{id}
</update>
<sql id="if-title">
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</sql>
<select ...>
...
<where>
<include refid="if-title"/>
</where>
</select>
foreach
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
WHERE ID in
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
<!-- (1,2,3,4,5) -->
</foreach>
</select
十二、缓存
MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。 为了使它更加强大而且易于配置,我们对 MyBatis 3 中的缓存实现进行了许多改进。
默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:
<cache/>
基本上就是这样。这个简单语句的效果如下:
- 映射语句文件中的所有 select 语句的结果将会被缓存。
- 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
- 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
- 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
- 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
- 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
1、一级缓存
SqlSession
.Post">
SELECT *
FROM POST P
WHERE ID in
#{item}
</select
# 十二、缓存
MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。 为了使它更加强大而且易于配置,我们对 MyBatis 3 中的缓存实现进行了许多改进。
默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:
```
基本上就是这样。这个简单语句的效果如下:
- 映射语句文件中的所有 select 语句的结果将会被缓存。
- 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
- 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
- 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
- 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
- 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。