前言

这个学期学校开始教javaee框架,虽说我之前已经稍微学习了解过ssm,但学习的过程以及内容挺碎片化的。现在再按学校教程顺序重新过一遍理论。从这篇博文开始记录一下学习ssm的整个过程。

mybatis简介

之前没有使用mybatis时通常都是使用jdbc的方法(主要的操作对象:Connection、Preparestatement、ResultSet)来操作数据库,每次执行sql操作都要频繁建立数据库连接,从而造成资源的浪费。mybatis是一种ORM框架,ORM框架可以完成对象模型和关系模型的映射。采用ORM框架后,应用程序不用再直接访问数据库,而是以面向对象的方法来操作持久化对象(PO),其通过面向对象的方法来代替程序操作底层SQL。简单理解就是mybatis不用执行jdbc方法中过多繁琐的操作,可以简化数据库操作。

mybatis的配置

  1. 如果在springboot项目中,只需在springboot整体的配置文件application.yml中配置即可
spring:
datasource:
username: 账号
password: 密码
url: jdbc:mysql://localhost:3306/数据库名?useSSL=false&serverTimezone=GMT #(如对应数据库有设置编码则需要说明characterEncoding)
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
mapper-locations: classpath:映射文件目录mapping所在(如:classpath:mapping/*Mapper.xml)
type-aliases-package: 数据库映射实体对象别名包(如:per.study.entity)
  1. 如果在其他项目中,则要额外编写配置文件(.xml),下面是一个简单的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>
<!--environments中default元素指向默认数据源(此处数据源为id为mybatis的数据源)-->
<environments default="mybatis">
<!--给数据源定id值-->
<environment id="mybatis">
<!--此处使用jdbc事务管理-->
<transactionManager type="JDBC"></transactionManager>
<!--配置数据库连接池参数-->
<!--此处有三个参数可供选择,POOLED、UNPOOLED、JNDI-->
<!--UNPOOLED每次被请求时都会打开和关闭,适用于没有性能要求的应用-->
<!--POOLED避免了创建新连接实例所必须的初始化和认证时间,使用于Web应用的快速响应方式-->
<!--JNDI这种数据源的实现是为了能和EJB或应用服务器这类容器中使用,容器可以集中或在外部配置数据源-->
<dataSource type="POOLED">
<!--驱动名(我使用的是mysql,对应以下驱动名)-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<!--mysql对应的url(如对应数据库有设置编码则需要说明characterEncoding)-->
<property name="url" value="jdbc:mysql://localhost:3306/test-mybatis?useSSL=false&amp;serverTimezone=GMT&amp;characterEncoding=utf8"/>
<!--数据库账号密码-->
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--mapper映射文件位置-->
<mappers>
<mapper class="mapper.BookMapper"></mapper>
</mappers>
</configuration>


mapper的使用

由于已经配置好配置文件,则我们可以开始关注具体业务的实现,不用再关心数据库连接的问题。

  1. mapper即是映射文件,通常命名为 实体名+Mapper.xml。mapper文件是sql语句的具体实现,增删改查的sql语句都会写在mapper里(非注解方式)。而对应的在程序里的接口类可以在mapper文件里指明来使用。

  2. 现在举个映射文件方式使用mapper的例子。(数据库all_comic表与Comic实体对应)

首先建个实体类Comic

//使用lombok可以自动生成set和get方法
@Data
public class Comic {
private Integer comicId;
private String comicName;
private String comicDescription;
private String comicCover;
private Date createTime;
private Date updateTime;
}

其对应的接口dao层

public interface ComicMapper {
//查询所有漫画
List<Comic> queryAll();
}

接口所对应的mapper,mapper标签的namespace要指定接口文件的位置,resultMap是定义返回的结果集,增删改查的语句就不用多说了

<?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.example.kbcomic.mapper.ComicMapper">
<resultMap id="comic" type="com.example.kbcomic.entity.Comic">
<!--column是数据库字段,property是实体属性-->
<result column="comic_id" property="comicId"></result>
<result column="comic_name" property="comicName"></result>
<result column="comic_description" property="comicDescription"></result>
<result column="comic_cover" property="comicCover"></result>
<result column="create_time" property="createTime"></result>
<result column="update_time" property="updateTime"></result>
</resultMap>
<!--查询所有漫画,id名要对应上接口方法名-->
<select id="queryAll" resultMap="comic">
select * from all_comic
</select>
</mapper>

根据上面mapper文件和mapper接口的映射,我们直接通过对象调用接口即可实现数据库操作

//先使用输入流读取配置文件
InputStream inputStream= Resources.getResourceAsStream("mybatis-config.xml");
//使用mybatis四大组件中的构造器创建工厂
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
//从输入流中获得配置文件
SqlSessionFactory factory = builder.build(inputStream);
//从工厂中获得sqlsession
SqlSession sqlSession = factory.openSession();
//获得session后可以对接mapper开始执行sql
ComicMapper mapper = sqlSession.getMapper(ComicMapper.class);
//执行查询语句
List<Comic> comics = mapper.queryAll();
//遍历集合打印
for(Comic comic:comics){
System.out.println(comic);
}
  1. 注解方式实现mapper
public interface ComicMapper {
//查询所有漫画
@Select("select * from all_comic")
//此处使用效果跟mapper文件中ResultMap一样,column指定数据库字段名,property指明对象实体属性
@Results(id = "comic",value = {
@Result(id = true,column = "comic_id",property = "comicId"),
@Result(column = "comic_name",property = "comicName"),
@Result(column = "comic_description",property = "comicDescription"),
@Result(column = "comic_cover",property = "comicCover"),
@Result(column = "create_time",property = "createTime"),
@Result(column = "update_time", property="updateTime")
})
List<Comic> queryAll();
}

由此可见使用注解方式不用再编写mapper.xml文件,更进一步简化整个流程。这也是通常大众所使用的方式。

mybatis中的缓存机制

mybatis缓存分为一级缓存和二级缓存,同时也可以配置关于缓存的设置。一级缓存是位于SqlSession上的缓存,二级缓存是在SqlSessionFactory上的缓存。通常情况下,mybatis会开启一级缓存,也就是处于SqlSession上的缓存,这个缓存不需要POJO对象可序列化。

  1. 二级缓存的开启

在mybatis的配置文件中的settings标签中添加二级缓存的开启

<configuration>
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>
</configuration>

(非注解方式)在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.example.kbcomic.mapper.ComicMapper">
<!--添加二级缓存声明-->
<cache></cache>
<resultMap id="comic" type="com.example.kbcomic.entity.Comic">
<!--column是数据库字段,property是实体属性-->
<result column="comic_id" property="comicId"></result>
<result column="comic_name" property="comicName"></result>
<result column="comic_description" property="comicDescription"></result>
<result column="comic_cover" property="comicCover"></result>
<result column="create_time" property="createTime"></result>
<result column="update_time" property="updateTime"></result>
</resultMap>
<!--查询所有漫画,id名要对应上接口方法名-->
<select id="queryAll" resultMap="comic">
select * from all_comic
</select>
</mapper>

(注解方式)添加二级缓存,在mapper接口上添加@CacheNamespace(blocking = true)或对应方法添加option注解

@CacheNamespace(blocking = true)
public interface ComicMapper {
//查询所有漫画
@Select("select * from all_comic")
//此处使用效果跟mapper文件中ResultMap一样,column指定数据库字段名,property指明对象实体属性
@Results(id = "comic",value = {
@Result(id = true,column = "comic_id",property = "comicId"),
@Result(column = "comic_name",property = "comicName"),
@Result(column = "comic_description",property = "comicDescription"),
@Result(column = "comic_cover",property = "comicCover"),
@Result(column = "create_time",property = "createTime"),
@Result(column = "update_time", property="updateTime")
})
//useCache=true启用二级缓存
@Options(useCache = true)
List<Comic> queryAll();
}
  1. 缓存方式的利弊

利:开启缓存后,第一次查询会执行sql,第二次及以后的查询都会从缓存中读取数据。减少了访问数据库的次数,优化系统性能。
弊:开启缓存的弊端是数据没有实时性,当数据库中的数据一旦修改,查询的数据还是缓存中的数据没有实时性。

  1. 对于缓存方式的弊端,有如下解决方案

(非注解方式)
①禁止缓存:在mapper文件select语句中设置useCache=”false”

<select id="queryAll" resultMap="comic" useCache="false">
select * from all_comic
</select>

②清空缓存:在insert或update语句中设置flushCache=”true”

<update id="updateComic" flushCache="true">
update all_comic
set comic_description=#{comic.comicDescription},comic_cover=#{comic.comicCover},update_time=#{comic.updateTime}
where comic_id=#{comicId}
</update>

(注解方式)

//在更新或插入时候刷新缓存
@Options(flushCache = Options.FlushCachePolicy.TRUE)
Integer updateComic(Comic comic);
//在查询时禁止缓存
@Options(useCache = false)
List<Book>queryAll();