Mybatis笔记
MyBatis文档网址:https://mybatis.org/mybatis-3/zh/index.html
文章目录
1. 创建第一个Mybatis程序
1.1 搭建环境
1.加入maven依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
2.在resources目录下,新建mybatis-config.xml,
3.并将官方文档的内容粘贴进去。
<?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="${driver}"/>
<!--driver: com.mysql.jdbc.Driver-->
<property name="url" value="${url}"/>
<!--url写成这样: jdbc:mysql://localhost:3306/表名?useSSL=false&useUnicode=true&characterEncoding=UTF-8-->
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>
<mappers>
<mapper resource=“org/mybatis/example/BlogMapper.xml”/>
</mappers>
注意: 这里的resource是用斜杠分隔的,并且要指向自己写的mapper.xml文件。
1.2 创建模块
1.创建utils类
2.分别获取SqlSessionFactory和SqlSession对象。
// SqlSessionFactory -->sqlSession
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
// 使用Mybatis第一步:
// 获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
// 使用Mybatis第二步:
// 从 SqlSessionFactory 中获取 SqlSession
// 既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。
// SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。
// 你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
1.3 写代码
1.编写实体类(各个字段与表对应起来)(为了防止出现问题,尽量将实体类序列化)
实现Serializable接口。
package com.hui.pojo;
// 实体类
public class User implements Serializable {
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;}
}
2.编写dao接口,也可以取名mapper
public interface UserMapper {
List<User> getUserList();
}
3.写接口对应的mapper.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">
<!--namespace=绑定一个对应的Dao/Mapper接口-->
<mapper namespace="com.hui.dao.UserMapper">
<!-- select查询语句 id:是需要重写的方法名字 resultType:是返回的类型,要自己指定全类名-->
<select id="getUserList" resultType="com.hui.pojo.User">
select * from mybatis.user
</select>
</mapper>
4.在pom文件中改一下资源路径 (重要)
<build>
<resources>
<resource>
<directory>src/main/java</directory><!--所在的目录-->
<includes><!--包括目录下的.properties,.xml 文件都会扫描到-->
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory><!--所在的目录-->
<includes><!--包括目录下的.properties,.xml 文件都会扫描到-->
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
5.编写测试类,全类名尽量与src一致(类名除外)
public class UserMapperTest {
@Test
public void test(){
// 第一步:获取SqlSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
// 方式一:getMapper
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
// 关闭SqlSession
sqlSession.close();
}
}
即可查询成功。
2. 增删改查CRUD
2.1 namespace
配置文件中namespace中的名称为对应Mapper接口或者Dao接口的完整包名,必须一致!(用点.分隔包名)
2.2 根据ID查用户
1.先在接口里面定义方法
2.在mapper.xml文件里面写查询语句,指定参数类型和返回值类型,传入的变量直接用 #{接口中参数变量} 即可
2.3 插入用户信息(增删改需要提交事务)
1.接口参数为User
2.在写配置文件的时候,指定参数类型是User,使用变量的时候可以直接使用User的属性。如下图:
3.测试代码(增删改需要提交事务)
2.4 关于事务的提交的两种方式
方式一:在编写代码的时候,写
sqlSession.commit();
方式二:在工具类中使用openSession的重载方法,参数为true即可。(参数的意思是autoCommit)
2.5 删、改(需要提交事务)
与以上增加类似。
delete
update
3. 使用Map去完成参数传递
1.接口中参数声明为Map形式
2.参数类型是小写的map, 里面值的类型可以是map的键
3.测试代码
小总结:
Map传递参数,直接在sql中取出key即可!【parameterType=“map”】
对象传递参数,直接在sql中取对象的属性即可! 【parameterType=“Object”】
只有一个基本类型参数 的情况下,可以直接在sql中取到!
多个参数:用Map,或者注解@Param("") 在下面9.2中有讲到!
4. 配置
4.1 核心配置文件
一般命名为:mybatis-config.xml
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
4.2 环境配置
MyBatis 可以配置成适应多种环境
不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
学会使用配置多套运行环境。
4.3 属性(properties)
可以引入外部的配置
1.新建db.properties文件,里面写上一些配置。
如果里面和外面的配置冲突了,优先使用外面的配置。
4.4 类型别名(typeAliases)
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。例如:
第一种写法:
<typeAliases>
<typeAlias alias="User" type="com.hui.pojo.User"/>
...
</typeAliases>
第二种写法:(扫描一个包)
<typeAliases>
<package name="domain.blog"/>
</typeAliases>
第三种写法:(注解方式)
直接在类的上面使用注解@Alias(“name”)
@Alias("author")
public class Author {
...
}
其他别名的映射:
别名 | 映射的类型 |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
object | Object |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
iterator | Iterator |
4.5 映射器(mappers)
指定mapper位置
方式一:使用相对于类路径的资源引用:
<!-- 使用相对于类路径的资源引用 -->
<configuration>
<mappers>
<mapper resource="com/hui/dao/UserMapper.xml"/>
...
</mappers>
</configuration>
方式二:使用映射器接口实现类的完全限定类名:
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
<mapper class="com.hui.dao.UserMapper"/>
...
</mappers>
对于方式二的注意点:
· 接口和他的Mapper配置文件必须同名!
· 接口和t
5. 生命周期
生命周期:
SqlSessionFactoryBuilder:
这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。
SqlSessionFactory:
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。
SqlSession:
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。
几种关系:
6. 结果映射
当表的字段名与实体类字段名不对应的时候,要进行映射。(在mapper.xml文件中)
实体类对应属性名property,数据库字段名column.
<resultMap id="userResultMap" type="User">
<id property="id" column="user_id" />
<result property="username" column="user_name"/>
<result property="password" column="hashed_password"/>
<!-- 当是对象类型的时候 -->
<association property="author" javaType="Author"/>
<!-- 当是集合类型的时候 -->
<collection property="posts" javaType="ArrayList" column="id" ofType="Post" select="selectPostsForBlog"/>
</resultMap>
然后就可以使用了
<select id="selectUsers" resultMap="userResultMap">
select user_id, user_name, hashed_password
from some_table
where id = #{id}
</select>
7.日志
7.1 标准的日志工程实现:
<settings>
<!--标准的日志工程实现-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
7.2 log4j
什么是Log4j?
·Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件
·我们也可以控制每一条日志的输出格式;
·通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。·通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
1.先导入log4j依赖
<dependencies>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
2.增加文件log4j.properties(可以写成其他的)
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.配置log4j为日志实现
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
4.简单使用
a.在需要使用Log4j的类中,导入包 import org.apache.log4j.Logger;
b.日志对象,参数为当前类的class
static Logger logger = Logger.getLogger(UserMapperTest.class);
c.举例
public class UserMapperTest {
static Logger logger = Logger.getLogger(UserMapperTest.class);
@Test
public void log4jTest(){
logger.info("info:进入了testLog4j");
logger.debug("debug:进入了testLog4j");
logger.error("error:进入了testLog4j");
}
}
8. 分页
8.1 mybatis分页的实现(传统的方式 limit)
首先mysql的分页,limit的用法
a.对于一个参数的情况就是显示出前n行数据
select * from product limit 3; // 显示前三行
b.limit有两个参数,第一个参数表示从第几行数据开始查,第二个参数表示查几条数据.
select * from product limit 3,2; //“limit 3,2”表示从第四行数据开始,取两条数据。
1.定义接口
2.xml文件实现
3.测试
9. 使用注解开发
9.1 注解开发
1.注解形式:(在接口文件里面直接添加注解)
public interface UserMapper {
@Select("select * from user")
List<User> getUsers();
}
2.在mybatis-config.xml文件中添加类路径映射,绑定接口。
<mappers>
<mapper class="com.hui.dao.UserMapper"></mapper>
</mappers>
本质上是利用Java反射,底层代理。
注意: 使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让你本就复杂的 SQL 语句更加混乱不堪。 因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句。
9.2 注解增删改查
有关#{} 和 ${}:
#{}是预编译的,可以有效防止sql注入。
有关@Param注解:
1.如果是一个参数,可以忽略注解,但是建议写上。
2.但是如果是多个参数,必须要使用 @Param 注解(基本类型和String得加,引用类型就不用加了)
3.@Param("")注解的意思是,上面的sql语句中用到的变量是从这个注解里取到的。(重要)
一些增删改查示例:
10.Lombok
除了日志功能,还有给实体类使用的@Data注解,省去写get、set方法。
如果实体类写完不想写get,set等等方法,只需要在实体类前加一个@Data注解,方法如下:
1.先确保有Lombok插件
2.pom文件中添加依赖
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
3.实体类前加注解@Data
可以查看:
注意:
@Data除了get、set方法外,还提供了equals、canEqual、hasCode、toString方法,以及无参的构造方法。
可以使用 @AllArgsConstructor 注解添加有参的构造方法,但此时无参的会消失。
可以在添加 @NoArgsConstructor 注解添加无参的构造方法。
11. 多对一的处理
现有Student表和Teacher表,他们是多对一的关系。
实体类:
@Data
public class Teacher{
private int tid;
private String tname;
}
@Data
public class Student{
private int sid;
private String sname;
private Teacher teacher;
}
11.1 方式一:
联合查询如下(子查询):
注意:在下面的子查询中,写的是id=#{id},而上面却是tid。
这是因为上面resultMap中对象的那个column只有一个参数,所以肯定会匹配到那个tid,也就是说id=#{id}这里大括号里的变量名取什么都行。
查询结果:
11.2 方式二:(按结果嵌套处理)(容易一点)
12. 一对多的处理
实体类:
@Data
public class Student{
private int sid;
private String sname;
private int tid;
}
@Data
public class Teacher{
private int tid;
private String tname;
private List<Student> students;
}
12.1 方式一:(嵌套查询)
12.2 方式二:(按结果嵌套处理)(容易一点)
小结:
1.关联- association【多对一】
2.集合- collection 【一对多】
3. javaType & ofType
1. JavaType 用来指定实体类中属性的类型.
2. ofType 用来指定映射到List或者集合中的pojo类型,泛型中的约束类型!
13. 动态SQL(重要)
什么是动态SQL:动态SQL就是指根据不同的条件生成不同的SQL语句。
if
choose (when, otherwise)
trim (where, set)
foreach
13.1 if
使用动态 SQL 最常见情景是根据条件包含 where 子句的一部分。比如:
<select id="getUsers" resultType="User">
SELECT * FROM user WHERE 1=1
<if test="title != null">
AND title like #{title}
</if>
</select>
<select id="getUsers" resultType="User">
SELECT * FROM user WHERE 1=1
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</select>
13.2 choose、when、otherwise
有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
若两者都没有传入,就返回标记为 featured 的 User。
<select id="getUsers" resultType="User">
SELECT * FROM user WHERE 1=1
<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>
13.3 trim、where、set
where 元素:
where元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
<select id="getUsers" resultType="User">
SELECT * FROM user
<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>
trim元素:
如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:
<trim prefix="WHERE" prefixOverrides="AND |OR "><!--前缀-->
...
</trim>
<trim prefix="SET" suffixOverrides=","><!--后缀-->
...
</trim>
提示: prefixOverrides 属性会忽略通过管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子会移除所有 prefixOverrides 属性中指定的内容,并且插入 prefix 属性中指定的内容。
set元素:
用于动态更新语句的类似解决方案叫做 set。set 元素可以用于动态包含需要更新的列,忽略其它不更新的列。
<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>
13.4 foreach
动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM user
WHERE id in
<foreach collection="ids" item="item" index="index"
open="(" separator="," close=")">
#{item}
</foreach>
</select>
举例:比如查询id为1 2 3 的博客(用or)
select * from user where (id=1 or id=2 or id=3)
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM user
<where>
<foreach collection="ids" item="id"
open="(" separator="or" close=")">
id=#{id}
</foreach>
</where>
</select>
提示: 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
13.5 script
要在带注解的映射器接口类中使用动态 SQL,可以使用 script 元素。
@Update({"<script>",
"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}",
"</script>"})
void updateAuthorValues(Author author);
13.6 <sql>标签(SQL片段)
使用<sql>标签可以把一些语句抽离出来。
提示: 最好基于单表来定义SQL片段。
14. 缓存
Mybatis 使用到了两种缓存:本地缓存(local cache)(一级缓存)和二级缓存(second level cache)。
14.1 一级缓存
一级缓存是默认开启的,关不掉。
只在一次sqlSession中有效,也就是拿到连接到关闭连接的这个区间段!
每当一个新 session 被创建,MyBatis 就会创建一个与之相关联的本地缓存。任何在 session 执行过的查询结果都会被保存在本地缓存中,所以,当再次执行参数相同的相同查询时,就不需要实际查询数据库了。本地缓存将会在做出修改、事务提交或回滚,以及关闭 session 时清空。
默认情况下,本地缓存数据的生命周期等同于整个 session 的周期。由于缓存会被用来解决循环引用问题和加快重复嵌套查询的速度,所以无法将其完全禁用。但是你可以通过设置 localCacheScope=STATEMENT 来只在语句执行时使用缓存。
注意,如果 localCacheScope 被设置为 SESSION,对于某个对象,MyBatis 将返回在本地缓存中唯一对象的引用。对返回的对象(例如 list)做出的任何修改将会影响本地缓存的内容,进而将会影响到在本次 session 中从缓存返回的值。因此,不要对 MyBatis 所返回的对象作出更改,以防后患。
你可以随时调用以下方法来清空本地缓存:
void clearCache()
确保 SqlSession 被关闭:
void close()
14.2 二级缓存
· 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存
· 基于namespace级别的缓存,一个名称空间,对应一个二级缓存
· 工作机制
· 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
· 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话提交或关闭的时候,一级缓存中的数据被保存到二级缓存中;
· 新的会话查询信息,就可以从二级缓存中获取内容;
· 不同的mapper查出的数据会放在自己对应的缓存(map)中;(也就是说只要开启了二级缓存,在同一个Mapper下就有效)
开启步骤:
1.开启全局缓存(mybatis-config.xml中setting里)
设置名 | 描述 | 有效值 | 默认值 |
---|---|---|---|
cacheEnabled | 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 | true | false | true |
2.在SQL映射文件(xxxmapper.xml)中添加一行:
<cache/>
也可以指定的详细一些:
<cache eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>