Mybatis学习笔记1
简介
是什么是Mybatis?
- MyBatis 是一款优秀的持久层框架
- 它支持自定义 SQL、存储过程以及高级映射
- MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作
- MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录
如何获得Mybatis
- maven仓库:
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
- Github:https://github.com/mybatis/mybatis-3
- 中文文档:https://mybatis.org/mybatis-3/zh/getting-started.html
持久层
数据持久化
持久化是将程序数据在持久状态和瞬时状态间转换的机制。
- 即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。持久化的主要应用是将内存中的对象存储在数据库中,或者存储在磁盘文件中、XML数据文件中等等
- JDBC就是一种持久化机制。文件IO也是一种持久化机制
持久层
- 完成持久化工作的代码块 . ----> dao层 【DAO (Data Access Object) 数据访问对象】
- 层界限十分明显,专注于数据持久化逻辑的实现
MyBatis第一个程序
1.数据库测试数据
2.配置文件导入相关jar包
<dependencies>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<!-- mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!-- junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
3.编写MyBatis核心配置文件
<!-- configuration核心配置文件 -->
<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=utf8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
</configuration>
4.编写MyBatis工具类
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.InputStream;
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 (Exception e){
e.printStackTrace();
}
}
// 获取SqlSession连接
//SqlSession 完全包含了面向数据库执行SQL命令所需的所有方法
public static SqlSession getSession(){
return sqlSessionFactory.openSession();
}
}
5.创建实体类
// 实体类
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;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
6.编写Mapper接口类
import com.NATE.pojo.User;
import java.util.List;
public interface UserDao {
List<User> selectUser();
}
7.编写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.NATE.dao.UserDao">
<!--select查询语句-->
<select id="getUserList" resultType="com.NATE.pojo.User">
select * from mybatis.user
</select>
</mapper>
注意:配置文件中namespace中的名称为对应Mapper接口或者Dao接口的完整包名,必须一致
8.编写测试类
public class UserDaoTest {
@Test
public void test(){
//获取SqlSession对象
SqlSession sqlSession = MybatisUtils.getSession();
//方式一:getMapper
UserDao userDao = sqlSession.getMapper(UserDao.class);
List<User> userList = userDao.getUserList();
for (User user : userList) {
System.out.println(user);
}
// 关闭SqlSession
sqlSession.close();
}
}
遇见的问题
1.无法读取Mapper.xml配置文件,因为Maven读取配置默认读取是在resources文件里,而Mapper.xml是写在java目录下,所以造成无法正确读取。
解决方案:开启Maven静态资源过滤,在pom.xml增加以下代码
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
2.Error querying database. Cause: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
运行报错如下:
在网上找了很多相关方法,基本上都是说因为MySQL服务器默认的“wait_timeout”是28800秒即8小时,意味着如果一个连接的空闲时间超过8个小时,MySQL将自动断开该连接,而连接池却认为该连接还是有效的(因为并未校验连接的有效性),当应用申请使用该连接时,就会导致上面的报错。提供的解决方案:1.修改my.conf配置文件,修改wait_timeout参数。2.在连接URL上添加参数:&autoReconnect=true&failOverReadOnly=false。
把网上的解决方法都试过还是无果,只要又回到报错问题上,逐步分析,从头到尾检查了好几遍代码,依旧没有发现问题,最后突然发现自己的Mysql是8.0版本,导入的mysql-connector-java包是5.0版本的。于是去Maven官网查了一下,5.0是15年的版本,更换到最新的版本后,没有再报连接失败的提示,报了新的错误:Error querying database. Cause: java.sql.SQLException: The server time zone value ‘�й���ʱ��’ is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the ‘serverTimezone’ configuration property) to use a more specifc time zone value if you want to utilize time zone support.
从错误上看应该是时区的错误,所以只要设置完毕系统的时区。这是在使用MySQL 8.0以上版本(MySQL连接驱动和版本都是8.0以上)的时候出现的问题错误,需要在访问数据库的Url后面加上以下的语句即可:
?serverTimezone=GMT%2B8
运行测试类,可以正取查询到数据,但还有一个报错,如下:
提示信息表明数据库驱动com.mysql.jdbc.Driver’已经被弃用了,应当使用新的驱动com.mysql.cj.jdbc.Driver’。所以,按照提示更改mybatis-config.xml配置信息,将com.mysql.jdbc.Driver 改为 com.mysql.cj.jdbc.Driver。
CRUD
1.select
选择,查询语句
- id:就是对应的namespace中的方法名
- resultType:sql语句执行的返回值
- parameterType:传入参数类型
测试:根据Id查询用户
1.在UserMaooer中添加对应方法
public interface UserMapper {
// 查询所有用户
List<User> getUserList();
//根据id查询用户
User getUserById(int id);
}
2.在UserMapper.xml中添加Select语句
<select id="getUserById" parameterType="int" resultType="com.NATE.pojo.User">
select * from mybatis.user where id = #{id}
</select>
3.测试类中测试
@Test
public void getUserById(){
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.getUserById(1);
System.out.println(user);
sqlSession.close();
}
2.insert
使用insert标签进行插入操作
测试:插入一个用户
1.在UserMapper接口中添加对应的方法
// 插入一个用户
int addUser(User user);
2.在UserMapper.xml中添加insert语句
<!-- 增加一个用户 -->
<!-- 对象中的属性,可以直接取出来 -->
<insert id="addUser" parameterType="com.NATE.pojo.User">
insert into mybatis.user (id,name,pwd) value (#{id},#{name},#{pwd});
</insert>
3.测试类中测试
// 增删改需要提交事务
// 增加一个用户
@Test
public void addUser(){
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.addUser(new User(4,"小冬","654123"));
// 提交事务
sqlSession.commit();
sqlSession.close();
}
3.upadte
使用update标签进行更新操作
测试:修改更新一个用户
1.在UserMapper接口中添加对应的方法
// 修改一个用户
int updateUser(User user);
2.在UserMapper.xml中添加update语句
<!-- 修改一个用户 -->
<update id="updateUser" parameterType="com.NATE.pojo.User">
update mybatis.user set name = #{name},pwd=#{pwd} where id=#{id} ;
</update>
3.测试类中测试
// 修改一个用户
@Test
public void updateUser(){
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.updateUser(new User(4,"小董","969632"));
// 提交事务
sqlSession.commit();
sqlSession.close();
}
4.delete
使用delete标签进行删除操作
测试:删除一个用户
1.在UserMapper接口中添加对应的方法
// 删除一个用户
int deleteUser(int id);
2.在UserMapper.xml中添加delete语句
<!-- 删除一个用户 -->
<delete id="deleteUser" parameterType="int" >
delete from mybatis.user where id = #{id}
</delete>
3.测试类中测试
// 删除一个用户
@Test
public void deleteUser(){
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.deleteUser(4);
// 提交事务
sqlSession.commit();
sqlSession.close();
}
5.多参数传递之Map方式
当实体类、数据库中的表,字段或者参数过多,应当考虑使用Map
测试:用Map传参进行增加用户
1.在UserMapper接口中添加对应的方法
// 用Map的方式增加一个用户
int addUser2(Map<String,Object> map);
2.在UserMapper.xml中添加insert语句(Map方式传参)
<!-- 传递Map的key -->
<insert id="addUser2" parameterType="map">
insert into mybatis.user(id,name,pwd) value (#{userid},#{username},#{password})
</insert>
3.测试类中测试
@Test
public void addUser2(){
//获取SqlSession对象
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("userid",4);
map.put("username","小姚");
map.put("password","512341");
mapper.addUser2(map);
sqlSession.commit();
// 关闭SqlSession
sqlSession.close();
}
6.模糊查询
1.在UserMapper接口中添加对应的方法
// 模糊查询
List<User> getUserLike(String value);
2.在UserMapper.xml中添加select语句
<!-- 模糊查询 -->
<select id="getUserLike" resultType="com.NATE.pojo.User">
select * from user where name like #{value}
</select>
3.在Java代码执行的时候,传递通配符%%
// 模糊查询
@Test
public void getUserLike(){
//获取SqlSession对象
SqlSession sqlSession = MybatisUtils.getSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userlist = mapper.getUserLike("%卷");
for (User user : userlist) {
System.out.println(user.getName());
}
sqlSession.close();
}
注意:在sql语句中拼接通配符,会引起sql注入
配置解析
核心配置文件(注意元素节点的顺序!顺序不对会报错)
mybatis-config.xml 系统核心配置文件,MyBatis 的配置文件包含其行为的设置和属性信息。可配置内容如下:
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
<!-- 注意元素节点的顺序!顺序不对会报错 -->
1.environment(环境配置)
MyBatis 可以配置成适应多种环境,不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
注意一些关键点:
- 默认使用的环境 ID(比如:default=“development”)
- 每个 environment 元素定义的环境 ID(比如:id=“development”)
- 事务管理器的配置(比如:type=“JDBC”)
- 数据源的配置(比如:type=“POOLED”)
默认环境和环境 ID 顾名思义。 环境可以随意命名,但务必保证默认的环境 ID 要匹配其中一个环境 ID
transactionManager(事务管理器)
在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):
- JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域
- MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。
dataSource(数据源)
- dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源
- 有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]")
- unpooled:这个数据源的实现只是每次被请求时打开和关闭连接
- pooled:这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来 , 这是一种使得并发 Web 应用快速响应请求的流行处理方式
- jndi:这个数据源的实现是为了能在如 Spring 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用
- 数据源也有很多第三方的实现,比如dbcp,c3p0,druid等等
2.properties(属性)
数据库这些属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,亦可通过 properties 元素的子元素来传递。可以来优化之前的配置文件
第一步:在资源目录下新建一个db.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
username=root
password=123456
第二步:将文件导入properties 配置文件
<!-- 引入properties配置文件 -->
<properties resource="db.properties"/>
<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>
也可以在 properties 元素的子元素中设置,如下:
<properties resource="db.properties">
<property name="username" value="root"/>
<property name="password" value="123456"/>
</properties>
若配置文件和子标签<property>
有相同字段,优先使用配置文件里的数据
3.typeAliases(类型别名)
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写
<typeAliases>
<!-- 给实体类起别名 -->
<typeAlias type="com.NATE.pojo.User" alias="user"/>
</typeAliases>
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean。每一个在包中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名(实际情况首字母大写也可以跑通)
<typeAliases>
<!-- 给实体类起别名 -->
<package name="com.NATE.pojo"/>
</typeAliases>
使用场景:
- 当实体类较少时,使用第一种;若实体类较多,使用第二种方式更加方便快捷。
- 第一种方式可以自定义取别名,第二种则不行;但若使用注解
@Alias
进行取别名,则第二种方式也可以自定义取别名
// 通过注解取别名
@Alias("user")
public class User {
}
4.settings(设置)
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。
官方文档:https://mybatis.org/mybatis-3/zh/configuration.html#settings
几个重要设置:
- 日志实现
- 懒加载
- 缓存开启关闭
一个配置完整的 settings 元素的示例如下:
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="false"/>
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25"/>
<setting name="defaultFetchSize" value="100"/>
<setting name="safeRowBoundsEnabled" value="false"/>
<setting name="mapUnderscoreToCamelCase" value="false"/>
<setting name="localCacheScope" value="SESSION"/>
<setting name="jdbcTypeForNull" value="OTHER"/>
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>
5.其它配置
类型处理器(typeHandlers)
- 无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型
- 可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型
对象工厂(objectFactory)
- MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成
- 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过有参构造方法来实例化
- 如果想覆盖对象工厂的默认行为,则可以通过创建自己的对象工厂来实现
6.mappers(映射器)
- 映射器 :定义映射SQL语句文件
- 既然 MyBatis 的行为其他元素已经配置完了,我们现在就要定义 SQL 映射语句了。但是首先我们需要告诉 MyBatis 到哪里去找到这些语句。Java 在自动查找这方面没有提供一个很好的方法,所以最佳的方式是告诉 MyBatis 到哪里去找映射文件。你可以使用相对于类路径的资源引用, 或完全限定资源定位符(包括
file:///
的 URL),或类名和包名等。映射器是MyBatis中最核心的组件之一,在MyBatis 3之前,只支持xml映射器,即:所有的SQL语句都必须在xml文件中配置。而从MyBatis 3开始,还支持接口映射器,这种映射器方式允许以Java代码的方式注解定义SQL语句,非常简洁。 - 引入资源方式:
1.相对路径方式
<!-- 使用相对于类路径的资源引用 -->
<mappers>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
2.绝对路径方式
<!-- 使用完全限定资源定位符(URL) -->
<mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
</mappers>
3.使用映射器接口实现类的完全限定类名
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
4.将包内的映射器接口实现全部注册为映射器
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>
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.NATE.mapper.UserMapper">
</mapper>
- namespace:命名空间
- namespace的命名必须跟某个接口同名
- 接口中的方法与映射文件中sql语句id应该一一对应
7.作用域(Scope)和生命周期
不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题
Mybatis的执行流程图
SqlSessionFactoryBuilder
- SqlSessionFactoryBuilder 的作用在于创建 SqlSessionFactory,创建成功后,SqlSessionFactoryBuilder 就失去了作用,所以它只能存在于创建 SqlSessionFactory 的方法中,而不要让其长期存在。因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。
SqlSessionFactory
- SqlSessionFactory 可以被认为是一个数据库连接池,它的作用是创建 SqlSession 接口对象。因为 MyBatis 的本质就是 Java 对数据库的操作,所以 SqlSessionFactory 的生命周期存在于整个 MyBatis 的应用之中,所以一旦创建了 SqlSessionFactory,就要长期保存它,直至不再使用 MyBatis 应用,所以可以认为 SqlSessionFactory 的生命周期就等同于 MyBatis 的应用周期。
- 由于 SqlSessionFactory 是一个对数据库的连接池,所以它占据着数据库的连接资源。如果创建多个 SqlSessionFactory,那么就存在多个数据库连接池,这样不利于对数据库资源的控制,也会导致数据库连接资源被消耗光,出现系统宕机等情况,所以尽量避免发生这样的情况。最简单的就是使用单例模式或者静态单例模式
SqlSession
- 连接到连接池的一个请求
- SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域
- 用完之后需要立刻关闭,否则资源被占用
这里的每一个Mapper,就代表一个具体的业务