https://mybatis.org/mybatis-3/zh/index.html
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
帮助程序员将数据存入到数据库中
传统的jdbc代码太复杂。简化。框架。自动化。
搭建数据库
新建普通maven项目,删src
导入依赖
<dependencies>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.47version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.2version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.13version>
<scope>testscope>
dependency>
dependencies>
每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。
从 XML 文件中构建 SqlSessionFactory 的实例非常简单,建议使用类路径下的资源文件进行配置。
1.resource文件下配置mybatis-config.xml
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="org/mybatis/example/BlogMapper.xml"/>
mappers>
configuration>
2.编写工具类
//sqlSessionFactory----->sqlSession(类比preparedStatement)
public class MybatisUtil {
private static SqlSessionFactory sqlSessionFactory = null;
static {
try {
//获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。
// SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。
// 你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。
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;
}
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;
}
}
Mapper接口(Dao)
List<User> getUserList();
Mapper.xml(Dao实现类)
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cc.dao.UserDao">
<select id="getUserList" resultType="com.cc.pojo.User">
select * from mybatis.user
select>
mapper>
在Mybatis-config.xml中注册Mapper:
<mappers>
<mapper resource="com/cc/dao/UserMapper.xml"/>
mappers>
junit
配置文件无法被导出或生效:
<build>
<resources>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.xmlinclude>
<include>**/*.propertiesinclude>
includes>
resource>
<resource>
<directory>src/main/resourcesdirectory>
<includes>
<include>**/*.xmlinclude>
<include>**/*.propertiesinclude>
includes>
resource>
resources>
build>
实体类中写toString方法,才能打印user
for (User user : userList) {
System.out.println(user);
}
<mapper namespace="com.cc.dao.UserMapper">
namespace中的包名要和Dao/mapper接口的包名一致
编写接口
User getUserById(int id);
编写实现
<select id="getUserById" parameterType="int" resultType="com.cc.pojo.User">
select * from mybatis.user where id = #{id}
select>
id:就是对应的namespace中的方法名
resultType:Sql语句执行的返回值
parameterType:参数类型
编写测试
//增删改需要提交事务
@Test
public void addUser(){
SqlSession sqlSession = MybatisUtil.getsqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.addUser(new User(4,"haha","123"));
//提交事务
sqlSession.commit();
sqlSession.close();
}
<insert id="addUser" parameterType="com.cc.pojo.User">
insert into mybatis.user (id, name, pwd) values (#{id},#{name},#{pwd})
insert>
<update id="updateUser" parameterType="com.cc.pojo.User">
update mybatis.user
set name = #{name},pwd = #{pwd}
where id = #{id};
update>
<delete id="deleteUser" parameterType="int">
delete
from mybatis.user
where id = #{id};
delete>
<insert id="addUser2" parameterType="map">
insert into mybatis.user(id,pwd) values (#{userid},#{password})
insert>
@Test
public void addUser2(){
SqlSession sqlSession = MybatisUtil.getsqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map map = new HashMap<>();
map.put("userid",5);
map.put("password","222233");
mapper.addUser2(map);
//提交事务
sqlSession.commit();
sqlSession.close();
}
<select id="getUserLike" resultType="com.cc.pojo.User">
select * from user where name like "%"#{value}"%"
select>
@Test
public void getUserLike(){
SqlSession sqlSession = MybatisUtil.getsqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userLike = mapper.getUserLike("李");
for (User user : userLike) {
System.out.println(user);
}
sqlSession.commit();
sqlSession.close();
}
或#{value} “%李%”
mybatis-config.xml
configuration(配置):
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量):
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。
driver=com.mysql.jdbc.Driverurl=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf-8username=truepassword=123456
在核心配置文件中引入(注意顺序)
<properties resource="db.properties"/>
1、类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。
<typeAliases>
<typeAlias type="com.cc.pojo.User" alias="User"/>
typeAliases>
<select id="getUserList" resultType="User">
2、或者指定实体类的包,mybatis会在包下搜索需要的JavaBean。类名首字母小写。
<typeAliases>
<package name="com.cc.pojo"/>
typeAliases>
<select id="getUserList" resultType="user">
实体类较少,使用第一种,实体类多,使用第二种
第一种可以diy别名
3、每一个在包 domain.blog
中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author
的别名为 author
;若有注解,则别名为其注解值。见下面的例子:
@Alias("author")
public class Author {
...
}
方式一:
<mappers>
<mapper resource="com/cc/mapper/UserMapper.xml"/>
mappers>
方式二:使用class文件绑定注册
<mappers> <mapper class="com.cc.mapper.UserMapper"/>mappers>
注意点:接口和它的Mapper配置文件必须同名且在同一个包下
方式三:使用扫描包进行注入绑定
<mappers> <package name="com.cc.mapper"/>mappers>
注意点:接口和它的Mapper配置文件必须同名且在同一个包下
不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题。
SqlSessionFactoryBuilder
SqlSessionFactory
SqlSession
每个Mapper代表一个具体业务
数据库中的字段:
测试实体类字段不一致的情况:
private int id;private String name;private String password;
select * from mybatis.user where id = #{id}即 select id,name,pwd from mybatis.user where id = #{id}
解决办法:
select id,name,pwd as password from mybatis.user where id = #{id}
结果集映射(显式)
<resultMap id="UserMap" type="User"> <result column="pwd" property="password"/>resultMap><select id="getUserById" resultMap="UserMap"> select * from mybatis.user where id = #{id}select>
resultMap
元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets
数据提取代码中解放出来,并在一些情形下允许你进行一些 JDBC 不支持的操作。
之前你已经见过简单映射语句的示例,它们没有显式指定 resultMap
。比如:
<select id="selectUsers" resultType="map">
上述语句只是简单地将所有的列映射到 HashMap
的键上,这由 resultType
属性指定。虽然在大部分情况下都够用,但是 HashMap 并不是一个很好的领域模型。
ResultMap
,再根据属性名来映射列到 JavaBean 的属性上。如果列名和属性名不能匹配上,可以在 SELECT 语句中设置列别名(这是一个基本的 SQL 特性)来完成匹配。<select id="selectUsers" resultType="User"> select user_id as "id", user_name as "userName", hashed_password as "hashedPassword" from some_table where id = #{id}select>
曾经:sout debug
现在:日志工厂
在Mybatis具体使用哪一个日志实现,在设置中设定
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
settings>
1.先导入LOG4J的包
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
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.配置log4j为日志的实现
<settings>
<setting name="logImpl" value="LOG4J"/>
settings>
4.LOG4J的使用
List<User> getUserByLimit(Map<String,Integer> map);
<select id="getUserByLimit" parameterType="map" resultMap="UserMap"> select * from user limit #{startIndex},#{pageSize}select>
@Test
public void getUserByLimit(){
SqlSession sqlSession = MybatisUtil.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> userByLimit = mapper.getUserByLimit(map);
for (User user : userByLimit) {
System.out.println(user);
}
sqlSession.close();
}
List<User> getUserByRowBounds();
<select id="getUserByLimit" resultMap="UserMap">
select * from user
select>
@Test
public void getUserByRowBounds(){
SqlSession sqlSession = MybatisUtil.getsqlSession();
//RowBounds实现
RowBounds rowBounds = new RowBounds(1,2);
//通过java代码层面实现分页
List<User> userList = sqlSession.selectList("com.cc.mapper.UserMapper.getUserByRowBounds",null,rowBounds);
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WZlXtBJ0-1643705679030)(https://gitee.com/roadside-stall/images/raw/master/imgs/20220130192709.png)]
1、注解在接口上实现
@Select("select * from user")List<User> getUsers();
2、需要再核心配置文件中绑定接口
<mappers> <mapper class="com.cc.mapper.UserMapper"/>mappers>
3、测试
@Testpublic void getUsers(){ SqlSession sqlSession = MybatisUtil.getsqlSession(); //底层主要应用反射 UserMapper mapper = sqlSession.getMapper(UserMapper.class); List users = mapper.getUsers(); for (User user : users) { System.out.println(user); } sqlSession.close();}
本质:反射机制实现
底层:动态代理
Mybatis详细的执行流程!
可以在工具类创建的时候实现自动提交事务
public static SqlSession getsqlSession(){
return sqlSessionFactory.openSession(true);
}
使用步骤:
1、在IDEA中安装Lombok插件
2、导入Lombok的jar包
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.22version>
dependency>
3、在实体类上加注解即可
@Data:无参构造,getter,setter,tostring,hashcode,equals
@AllArgsConstructor有参
@NoArgsConstructor无参
@Getter and @Setter
@ToString
@EqualsAndHashCode
@AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor
@Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
@Data
@Builder
一个老师---->多个学生
1、导入Lombok
2、新建实体类Teacher Student
3、建立Mapper接口
4、建立Mapper.xml文件
5、在核心配置文件中绑定注册Mapper
6、测试查询
第一种:select id,name,tid from student where tid=(select id from teacher)
<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 = #{tid}
select>
第二种:select s.id,s.name,t.name from student s,teacher t where s.tid=t.id
<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>
实体类
@Data
public class Student {
private int id;
private String name;
//学生关联一个老师
private int tid;
}
@Datapublic class Teacher { private int id; private String name; //一个老师拥有多个学生 private List students;}
<select id="getTeacher" resultMap="TeacherStudent">
select s.id sid,s.name sname,t.name tname,t.id tid
from teacher t,student s
where s.tid = t.id and t.id = #{tid}
select>
<resultMap id="TeacherStudent" type="Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<collection property="students" ofType="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
collection>
resultMap>
<select id="getTeacher2" resultMap="TeacherStudent2">
select * from teacher where id = #{tid}
select>
<resultMap id="TeacherStudent2" type="Teacher">
<collection property="students" column="id" javaType="ArrayList" ofType="Student" select="getStudentByTeacherId"/>
resultMap>
<select id="getStudentByTeacherId" resultType="Student">
select * from student where tid = #{id}
select>
小结
1.关联-association 多对一
2.集合-collection 一对多
3.JavaType & ofType
javaType:用来指定实体类中属性的类型
ofType:用来指定映射到List或者集合中的pojo类型,泛型中的约束类型
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;
}
List<Blog> queryBlogIF(Map map);
<select id="queryBlogIF" parameterType="map" resultType="blog">
select * from blog where 1=1
<if test="title != null">
and title = #{title}
if>
<if test="author != null">
and author = #{author}
if>
select>
@Testpublic void queryBlogIF(){ SqlSession sqlSession = MybatisUtil.getsqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); HashMap hashMap = new HashMap(); hashMap.put("title","Java"); hashMap.put("author","狂神说"); List<Blog> blogs = mapper.queryBlogIF(hashMap); for (Blog blog : blogs) { System.out.println(blog); } sqlSession.close();}
<select id="queryBlogChoose" parameterType="map" resultType="blog"> select * from blog <where> <choose> <when test="title != null"> title = #{title} when> <when test="author != null"> and author = #{author} when> <otherwise> and views = #{views} otherwise> choose> where>select>
<update id="updateBlog" parameterType="map">
update blog
<set>
<if test="title!=null">
title = #{title},
if>
<if test="author!=null">
author = #{author},
if>
set>
where id = #{id}
update>
动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。
List<Blog> queryBlogForeach(Map map);
eg. select * from blog where 1=1 and (id=1 or id=2 or id=3)
<select id="queryBlogForeach" parameterType="map" resultType="blog">
select * from blog
<where>
<foreach collection="ids" item="id" open="and (" close=")" separator="or">
id = #{id}
foreach>
where>
select>
@Test
public void queryBlogForeach(){
SqlSession sqlSession = MybatisUtil.getsqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap();
ArrayList<Integer> ids = new ArrayList<>();
ids.add(1);
map.put("ids",ids);
List<Blog> blogs = mapper.queryBlogForeach(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
<sql id="if-title-author">
<if test="title!=null">
title = #{title},
if>
<if test="author!=null">
author = #{author},
if>
sql>
<update id="updateBlog" parameterType="map">
update blog
<set>
<include refid="if-title-author">include>
set>
where id = #{id}
update>
动态SQL就是在拼接SQL语句
建议:
测试步骤:
1.开启日志
2.测试在一个session中查询两次相同记录
3.查看日志输出
缓存失效情况:
1.查询不同东西
2.增删改操作,可能会改变原来的数据,所以必定刷新缓存
3.查询不同的Mapper.xml
4.手动清理缓存
sqlSession.clearCache();//手动清理
小结:一级缓存默认开启,只在一次SqlSession中有效,也就是拿到连接到关闭连接这个区间段
要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:
<cache/>
这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。
可用的清除策略有:
LRU
– 最近最少使用:移除最长时间不被使用的对象。FIFO
– 先进先出:按对象进入缓存的顺序来移除它们。SOFT
– 软引用:基于垃圾回收器状态和软引用规则移除对象。WEAK
– 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。默认的清除策略是 LRU。
步骤:
1.开启全局缓存
<setting name="cacheEnable" value="true"/>
2.在要使用二级缓存中的Mapper中开启
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
3.测试
问题:只写标签,需要将实体类序列化!
readOnly默认为false,只读不报错,可读写的缓存会通过序列化返回缓存对象的拷贝,此时需要实体类实现Serializable接口或配置 readOnly=“true”
小结
只要开启了二级缓存,在同一个Mapper下就有效
所有的数据都会先放在一级缓存中
只有当会话提交或关闭的时候,才会提交到二级缓存中
查找顺序:先看二级缓存中有没有,再看一级缓存中有没有,再查询数据库
ehcache是一种广泛使用的开源Java分布式操作,主要面向通用缓存
1.导包
<dependency>
<groupId>org.mybatis.cachesgroupId>
<artifactId>mybatis-ehcacheartifactId>
<version>1.2.1version>
dependency>
2.在mapper中指定使用ehcache缓存实现
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
3.配置文件ehcache.xml
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<diskStore path="./tmpdir/Tmp_EhCache"/>
<defaultCache
eternal="false"
maxElementsInMemory="10000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="259200"
memoryStoreEvictionPolicy="LRU"/>
<cache
name="cloud_user"
eternal="false"
maxElementsInMemory="5000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="1800"
memoryStoreEvictionPolicy="LRU"/>
ehcache>