目录
- 1. 简介
- 2. 第一个MyBatis程序
- 3. 增删改查实现(CRUD)
- 4. 配置解析
- 4.1 [环境配置(environments)](https://mybatis.org/mybatis-3/zh/configuration.html#environments)
- 4.2 事务管理器(transactionManager)
- 4.3 数据源(dataSource)
- 4.4 [属性(properties)](https://mybatis.org/mybatis-3/zh/configuration.html#properties)
- 4.5 [类型别名(typeAliases)](https://mybatis.org/mybatis-3/zh/configuration.html#typeAliases)
- 4.6 [设置(settings)](https://mybatis.org/mybatis-3/zh/configuration.html#settings)
- 4.7 [映射器(mappers)](https://mybatis.org/mybatis-3/zh/configuration.html#mappers)
- 4.8 其他配置
- 5. 日志工厂
1. 简介
1.1 什么是MyBatis
官网文档: https://mybatis.org/mybatis-3/zh/index.html
- MyBatis 是一款优秀的持久层框架
- 它支持自定义 SQL、存储过程以及高级映射。
- MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
- MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
- MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
- iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAOs)
如何获取MyBatis?
-
GitHub: https://github.com/mybatis/mybatis-3
是一个Maven项目
jar包与源码:https://github.com/mybatis/mybatis-3/releases
即使不用jar包,把源码来过来也能用。 -
Maven仓库:https://mvnrepository.com/search?q=MyBatis
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.8</version> </dependency>
1.2 持久化
数据持久化:
- 持久化就是将程序的数据在持久状态与瞬时状态转化的过程。
- 内存:“断电即失”
- 数据库(JDBC),io文件持久化
为什么要持久化?
- 有一些对象不能让它丢掉
- 内存“断电即失”且太贵
1.3 持久层
Dao层、Server层、Controler层…
- 完成持久化工作的代码块
- 层是界限十分明显的
1.4 为什么需要MyBatis?
- 传统的JDBC代码太复杂
- 帮助程序员将数据存入数据库
优点:
- 简单易学
- 代码与sql分离,提高可维护性
- 提供映射标签,支持对象与数据库的orm字段关系映射
- 提供对象关系映射标签,支持对象关系组建维护
- 提供xml标签,支持编写动态sql
2. 第一个MyBatis程序
思路:搭建环境–>导入MyBatis–>编写代码–>测试
2.1 搭建环境
-
搭建数据库
CREATE DATABASE `mybatis`; USE `mybatis`; CREATE TABLE IF NOT EXISTS `user`( `id` INT(20) NOT NULL, `name` VARCHAR(30) DEFAULT NULL, `pwd` VARCHAR(30) DEFAULT NULL, PRIMARY KEY(`id`) )ENGINE=INNODB,DEFAULT CHARSET=utf8; INSERT INTO `user` (`id`, `name`, `pwd`)VALUES (1, '张三', '1234'), (2, '李四', '1235'), (3, '王二', '1236')
2.2 创建项目
-
新建一个普通的Maven项目
-
删除src目录,该项目就作为一个父工程,再创建子模块后,子模块就不用再重复导包
-
导入依赖
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <!--父工程--> <groupId>com.sugar</groupId> <artifactId>MyBatis-Study</artifactId> <version>1.0-SNAPSHOT</version> <!--导入依赖--> <dependencies> <!--MySQL驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.17</version> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.5</version> </dependency> <!--junit测试包--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies> </project>
2.3 创建一个模块
-
编写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="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&serverTimezone=GMT%2B8"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> </configuration>
-
编写MyBatis工具类
package com.sugar.utils; 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.IOException; import java.io.InputStream; /** * @program: MyBatis-Study * @description: MyBatis工具类 * @author: SuGar * @create: 2021-12-24 16:53 **/ 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(); } } // 既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。 // SqlSession 提供了在数据库执行SQL命令所需的所有方法。你可以通过SqlSession实例来直接执行已映射的SQL语句 public static SqlSession getSqlSession() { return sqlSessionFactory.openSession(); } }
2.4 编写代码
-
实体类
package com.sugar.pojo; /** * @program: MyBatis-Study * @description: 用户实体类 * @author: SuGar * @create: 2021-12-24 17:10 **/ 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 + '\'' + '}'; } }
-
Dao接口,MyBatis中用xxxMapper.java
package com.sugar.dao; import com.sugar.pojo.User; import java.util.List; public interface UserDao { List<User> getUserList(); }
-
接口实现类:不再用xxxImpl.java,而用Mapper.xml
package com.sugar.dao; import com.sugar.pojo.User; import java.util.List; /** * @program: MyBatis-Study * @description: 旧的基于JDBC的方式,与Mapper.xml功能对应 * @author: SuGar * @create: 2021-12-24 17:21 **/ public class UserDaoImpl implements UserDao{ @Override public List<User> getUserList() { // 执行sql String sqp = "select * from mybatis.user"; // 结果集:ResulySet return null; } }
<?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.sugar.dao.UserDao"> <!--id对应接口名称,然后主要有resultType和resultMap,对应java实体类--> <select id="getUserList" resultType="com.sugar.pojo.User"> SELECT * FROM mybatis.user </select> </mapper>
2.5 测试
注意点1:
org.apache.ibatis.binding.BindingException: Type interface com.sugar.dao.UserDao is not known to the MapperRegistry.
MapperRegistry是什么?
Mapper注册,每一个xxxMapper.xml文件都需要在MyBatis核心配置文件MyBatis-config,xml中注册:
<?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="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!--每一个Mapper.xml都需要再该文件中注册-->
<mappers>
<mapper resource="com/sugar/dao/UserMapper.xml"/>
</mappers>
</configuration>
注意点2:
一般xxxMapper.xml文件放在resources目录下,但现在放在了src/main/java/…目录下,Maven由于它的约定大于配置,会导致找不到注册的Mapper文件,即在target文件夹下没有xxxMapper.xml文件,这是Maven会常出现的问题。
java.io.IOException: Could not find resource com/sugar/dao/UserMapper.xml
此时需要进行Maven资源过滤:
在主工程的pom.xml文件中进行配置:
(目前只在主工程配置了,若以后遇到问题,在子工程也配置试试)
<!--在build中配置resources来防止我们资源导出失效的问题-->
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
-
Junit测试
-
在test文件夹下的java文件夹下创建与src/…/java文件夹一样的包结构
-
创建类:UserDaoTest(要测试UserDao中的接口)
package com.sugar.dao; import com.sugar.pojo.User; import com.sugar.utils.MyBatisUtils; import org.apache.ibatis.session.SqlSession; import org.junit.Test; import java.util.List; /** * @program: MyBatis-Study * @description: UserDao接口的测试 * @author: SuGar * @create: 2021-12-24 17:39 **/ public class UserDaoTest { @Test public void test() { //第一步:获取SqlSession对象 SqlSession sqlSession = MyBatisUtils.getSqlSession(); //第二步:执行sql //方式一:getMapper UserDao userDao = sqlSession.getMapper(UserDao.class); List<User> userList = userDao.getUserList(); for (User user : userList) { System.out.println(user); } //最后:关闭SqlSession sqlSession.close(); } }
输出:
-
2.6 三大核心接口
(1)SqlSessionFactoryBuilder 使用后就丢弃
这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。 你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。
(2)SqlSessionFactory 创建一次一直用
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建 SqlSessionFactory 被视为一种代码“坏习惯”。因此 SqlSessionFactory 的最佳作用域是应用作用域。 有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。
(3)SqlSession 一个连接用一个
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。 下面的示例就是一个确保 SqlSession 关闭的标准模式:
try (SqlSession session = sqlSessionFactory.openSession()) {
// 你的应用逻辑代码
} catch (Exception e) {
e.printStackTrace();
} finally {
session.close();
}
的作用域是请求或方法作用域**。 绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。 也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。 这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。 下面的示例就是一个确保 SqlSession 关闭的标准模式:
try (SqlSession session = sqlSessionFactory.openSession()) {
// 你的应用逻辑代码
} catch (Exception e) {
e.printStackTrace();
} finally {
session.close();
}
在所有代码中都遵循这种使用模式,可以保证所有数据库资源都能被正确地关闭。
2.7 作用域(Scope)和生命周期
理解我们之前讨论过的不同作用域和生命周期类别是至关重要的,因为错误的使用会导致非常严重的并发问题。
3. 增删改查实现(CRUD)
3.1 接口xxxMapper/Dao.java
package com.sugar.dao;
import com.sugar.pojo.User;
import java.util.List;
public interface UserMapper {
// 查询所有用户
List<User> getUserList();
// 根据id查询用户
User getUserById(int id);
// 新增用户
int addUser(User user);
// 修改用户
int updateUser(User user);
// 删除用户
int deleteUser(int id);
}
3.2 xxxMapper.xml
-
namespace
namespace值要与Dao/Mapper的报名一致!<mapper namespace="com.sugar.dao.UserMapper"> <select id="xxx" parameterType="xxx" resultType="xxx"> ... </select> <insert id="xxx" parameterType="xxx"> ... </insert> <delete id="xxx" parameterType="xxx"> ... </delete> <update id="xxx" parameterType="xxx"> ... </update> </mapper>
-
select标签
查询语句
id:Mapper中的方法名
resultType:sql语句返回值类型
parameterType:参数值类型<!--根据id查询用户--> <!--parameterType="int" 不写也可以--> <select id="getUserById" parameterType="int" resultType="com.sugar.pojo.User"> SELECT * FROM user WHERE id = #{id} </select>
-
insert标签
需要提交事务
插入语句!--插入用户--> <!--对象的属性可以直接用作参数--> <insert id="addUser" parameterType="com.sugar.pojo.User"> INSERT INTO user (id, name, pwd) VALUES (#{id}, #{name}, #{pwd}) </insert>
-
delete标签
需要提交事务
删除语句<!--删除用户--> <delete id="deleteUser" parameterType="com.sugar.pojo.User"> DELETE FROM user WHERE id = #{id} </delete>
-
update标签
需要提交事务
修改语句<!--修改用户--> <update id="updateUser" parameterType="com.sugar.pojo.User"> UPDATE user set name = #{name}, pwd = #{pwd} where id = #{id}; </update>
-
提交事务
@Test public void addUser(){ //第一步:获取SqlSession对象 SqlSession sqlSession = MyBatisUtils.getSqlSession(); try{ //第二步:执行sql UserMapper userMapper = sqlSession.getMapper(UserMapper.class); int res = userMapper.addUser(new User(5, "新增5", "123456")); if (res > 0) { System.out.println("插入成功!"); } // 增删改需要提交事务,不然,即使res>0也插不进去 sqlSession.commit(); }catch (Exception e) { e.printStackTrace(); }finally{ //最后:关闭SqlSession sqlSession.close(); } }
3.3 Map作参数类型
-
mapper.java
package com.sugar.dao; import com.sugar.pojo.User; import java.util.List; import java.util.Map; public interface UserMapper { // 新增用户 int addUser2(Map<String, Object> map); }
-
Mapper.xml
<!--使用map作为参数类型,value的字段不需要与User对象的相同,而要与map的键相同--> <insert id="addUser2" parameterType="map"> INSERT INTO user (id, name, pwd) VALUES (#{uid}, #{uname}, #{upwd}) </insert>
-
测试
package com.sugar.dao; import com.sugar.pojo.User; import com.sugar.utils.MyBatisUtils; import org.apache.ibatis.session.SqlSession; import org.junit.Test; import java.sql.SQLPermission; import java.util.HashMap; import java.util.Map; /** * @program: MyBatis-Study * @description: UserDao接口的测试 * @author: SuGar * @create: 2021-12-24 17:39 **/ public class UserDaoTest { @Test public void addUser2(){ //第一步:获取SqlSession对象 SqlSession sqlSession = MyBatisUtils.getSqlSession(); try{ //第二步:执行sql UserMapper userMapper = sqlSession.getMapper(UserMapper.class); // Map<String, Object> userMap = new HashMap<>(); userMap.put("uid", 7); userMap.put("uname", "老七"); userMap.put("upwd", "777"); / int res = userMapper.addUser2(userMap); if (res > 0) { System.out.println("插入成功!"); } // 增删改需要提交事务,不然,即使res>0也插不进去 sqlSession.commit(); }catch (Exception e) { e.printStackTrace(); }finally{ //最后:关闭SqlSession sqlSession.close(); } } }
3.4 模糊查询
-
mapper.java
package com.sugar.dao; import com.sugar.pojo.User; import java.util.List; public interface UserMapper { // 姓名模糊查询 List<User> getUserByNameLike(String val); }
-
mapper.xml
<!--姓名模糊查询--> <select id="getUserByNameLike" resultType="com.sugar.pojo.User"> SELECT * FROM user WHERE name LIKE "%"#{val}"%" </select>
-
测试
@Test public void getUserByNameLike(){ SqlSession sqlSession = MyBatisUtils.getSqlSession(); try{ UserMapper userMapper = sqlSession.getMapper(UserMapper.class); List<User> users = userMapper.getUserByNameLike("老"); for (User user : users) { System.out.println(user); } }catch (Exception e) { e.printStackTrace(); }finally{ sqlSession.close(); } }
-
防止sql注入问题
sql注入:select * from user where id = ?;
上面的sql在传参时,不做处理,“?”会被利用:
让?= 3 or 1 = 1,那么,会得到全部结果select * from user where id = 3 or 1 = 1;
解决:
- 在sql中写死通配符“%”,让用户不能传递多余的东西
<!--姓名模糊查询--> <select id="getUserByNameLike" resultType="com.sugar.pojo.User"> SELECT * FROM user WHERE name LIKE "%"#{val}"%" </select>
- 在java中传递通配符,让用户不能传递多余的东西
@Test public void getUserByNameLike(){ SqlSession sqlSession = MyBatisUtils.getSqlSession(); try{ UserMapper userMapper = sqlSession.getMapper(UserMapper.class); List<User> users = userMapper.getUserByNameLike("%老%"); for (User user : users) { System.out.println(user); } }catch (Exception e) { e.printStackTrace(); }finally{ sqlSession.close(); } }
- 在sql中写死通配符“%”,让用户不能传递多余的东西
3.5 ResultMap
解决属性与字段不一致问题。
属性与字段不一致,如:
数据库为:
而实体类为:
public class User {
private int id;
private String name;
private String password;//此处不一样
}
查询结果:password属性为null
解决办法:
-
SQLy语句取别名:
SELECT id, name, pwd as password FROM user WHERE id = #{id}
-
resultMap:
只有password与pwd不一样,映射它就行<!--结果集映射--> <resultMap id="UserMap" type="User"> <result property="password" column="pwd"/> </resultMap> <select id="getUserById" parameterType="int" resultMap="UserMap"> SELECT * FROM user WHERE id = #{id} </select>
3.6 分页查询
为啥要分页?
减少数据处理量。
sql分页方法:limit
1. limit
#startIndex从0开始
select * from user limit startIndex, pageSize;
UserMapper.java接口:
// 分页查询
List<User> getUserByLimit(Map<String, Integer> map);
UserMapper.xml sql语句:
<!--分页查询-->
<select id="getUserByLimit" parameterType="map" resultMap="UserMap">
SELECT * FROM user limit #{startIndex}, #{pageSize}
</select>
测试:
@Test
public void getUserByLimit() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
try {
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String, Integer> map = new HashMap<>();
map.put("startIndex", 0);
map.put("pageSize", 4);
List<User> users = mapper.getUserByLimit(map);
for (User user : users) {
System.out.println(user);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
sqlSession.close();
}
}
2. rowBounds 【了解】
不再通过sql实现分页
UserMapper.java接口:
// 分页查询2
List<User> getUserByRowBounds();
UserMapper.xml sql语句:
<!--分页查询2-->
<select id="getUserByRowBounds" resultMap="UserMap">
SELECT * FROM user
</select>
测试:
@Test
public void getUserByRowBounds() {
SqlSession sqlSession = MyBatisUtils.getSqlSession();
try {
RowBounds rowBounds = new RowBounds(0, 4);
List<User> users = sqlSession.selectList("com.sugar.dao.UserMapper.getUserByRowBounds", null, rowBounds);
for (User user : users) {
System.out.println(user);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
sqlSession.close();
}
}
3. 分页插件
4. 配置解析
-
mybatis-config.xml
-
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下:
configuration(配置) properties(属性) settings(设置) typeAliases(类型别名) typeHandlers(类型处理器) objectFactory(对象工厂) plugins(插件) environments(环境配置) environment(环境变量) transactionManager(事务管理器) dataSource(数据源) databaseIdProvider(数据库厂商标识) mappers(映射器)
4.1 环境配置(environments)
MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中。
不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
- 每个数据库对应一个 SqlSessionFactory 实例
- 配置多套环境
配置多个environment标签即可 - 设置默认环境
environments标签的default属性值 - java中选择环境
SqlSessionFactory 实例只能选择一种环境,在构建SqlSessionFactory 时可以通过参数设置:String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); // build方法的第二个参数可以设置目标环境 sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream, "development");
4.2 事务管理器(transactionManager)
有JDBC和MANAGED,默认JDBC,spring默认JDBC
-
JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
-
MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。
<transactionManager type="MANAGED"> <property name="closeConnection" value="false"/> </transactionManager>
4.3 数据源(dataSource)
有三种:
- UNPOOLED:没有池的概念
- POOLED:引入池的概念
- JNDI
池:与线程池类似,用完不关闭,等待其他连接使用。
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
4.4 属性(properties)
这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。如db.properties文件
-
编写一个db.properties文件
driver=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8 username=root password=root
-
在核心配置文件中引入外部配置文件
<?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"/> <!--default="development",默认环境--> <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> <!--每一个Mapper.xml都需要再该文件中注册--> <mappers> <mapper resource="com/sugar/dao/UserMapper.xml"/> </mappers> </configuration>
-
若没有编写外部配置文件或外部配置文件中没有部分属性,可以通过在environments标签前增加properties标签编写属性。同时,引入外部配置文件也是通过properties标签。
没有编写外部配置文件<properties> <property name="driver" value="com.mysql.cj.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8"></property> <property name="username" value="root"></property> <property name="password" value="root"></property> </properties>
外部配置文件没有部分属性
<properties resource="db.properties"> <property name="username" value="root"></property> <property name="password" value="root"></property> </properties>
4.5 类型别名(typeAliases)
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。
方法一:每个实体类配置一个别名
在核心配置文件中,按顺序写typeAliases标签
<!--给实体类取别名-->
<typeAliases>
<!--方法一:每个实体类取一个别名-->
<typeAlias type="com.sugar.pojo.User" alias="User"/>
</typeAliases>
然后在Mapper.xml中使用别名
方法二:扫描包自动获取别名
<!--给实体类取别名-->
<typeAliases>
<!--方法而:扫描包,自动获取其中的类的别名,若没有注解,默认以类名首字母小写为别名-->
<package name="com.sugar.pojo"/>
</typeAliases>
在实体类中加注解
// Mybatis扫描包获取别名注解,在Mapper.xml中使用。若不注解,默认用首字母小写的类名作别名
@Alias("User")
public class User {
private int id;
private String name;
private String pwd;
...
4.6 设置(settings)
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。
有很多个,在官网文档。
记住几个:
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="mapUnderscoreToCamelCase" value="false"/>
</settings>
4.7 映射器(mappers)
配置完属性后,要来定义 SQL 映射语句了。 但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。在核心配置文件中进行配置
方法一:资源路径【推荐】
<mappers>
<mapper resource="com/sugar/dao/UserMapper.xml"/>
</mappers>
方法二:通过class文件
<mappers>
<mapper class="com.sugar.dao.UserMapper"/>
</mappers>
- 需要接口与mapper.xml文件同名!
- 需要接口与mapper.xml文件在同一包下!
方法三:通过包扫描
<mappers>
<package name="com.sugar.dao"/>
</mappers>
- 需要接口与mapper.xml文件同名!
- 需要接口与mapper.xml文件在同一包下!
4.8 其他配置
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件):
- mybatis-generator-core:根据数据库生成Mybatis代码
- mybatis-plus:简化Mybatis的工具
- 通用mapper
5. 日志工厂
解决数据库操作异常时查错:
- java输出,sout
- 日志工厂
通过设置(Setting)中的logImpl实现:
- SLF4J
- LOG4J(deprecated since 3.5.9) 【掌握】
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING 【掌握】
- NO_LOGGING
没有配置日志时:
5.1 STDOUT_LOGGING 【掌握】
1. 配置
注意规范,别写错
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
2. 运行结果
5.2 LOG4J
- Log4j是Apache的一个开源项目;
- 可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;
- 可以控制每一条日志的输出格式;定义每一条日志信息的级别;
- 可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
从Mybatis 3.5.9开始不再支持,所以得用低版本的Mybatis
1. 导包
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</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/sugar.log
log4j.appender.file.MaxFileSize = 10mb #文件超过10M就生成新的文件
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
2.2 日志等级
OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL
每个等级包含其左边的。如定义为INFO,那么,会输出ERROR、WARN、INFO的日志。
2.3 目的地
org.apache.log4j.ConsoleAppender(控制台),
org.apache.log4j.FileAppender(文件),
org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件),
org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件)
org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)
2.4 layuot
org.apache.log4j.HTMLLayout(以HTML表格形式布局),
org.apache.log4j.PatternLayout(可以灵活地指定布局模式),
org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串),
org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)
2.5 打印信息的具体格式ConversionPattern
log4j.appender.stdout.layout.ConversionPattern= [QC] %p [%t] %C.%M(%L) | %m%n
如果使用pattern布局就要指定的打印信息的具体格式ConversionPattern,打印参数如下:
%m 输出代码中指定的消息;
%M 输出打印该条日志的方法名;
%p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL;
%r 输出自应用启动到输出该log信息耗费的毫秒数;
%c 输出所属的类目,通常就是所在类的全名;
%t 输出产生该日志事件的线程名;
%n 输出一个回车换行符,Windows平台为"rn”,Unix平台为"n”;
%d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyyy-MM-dd HH:mm:ss,SSS},输出类似:2002-10-18 22:10:28,921;
%l 输出日志事件的发生位置,及在代码中的行数;
[QC]是log信息的开头,可以为任意字符,一般为项目简称。
3. 设置日志工厂实现为log4j
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
4. 运行结果
与STDOUT_LOGGING相比,多了许多前缀。
同时,生成了日志文件:
5. 在java代码中的简单使用
1. 创建对象
在哪个类使用,就在哪个类创建日志对象。
static Logger logger = Logger.getLogger(UserDaoTest.class);
2. 使用
@Test
public void testLog4j(){
logger.info("info");
logger.debug("debug");
logger.error("error");
}
- logger.info(“message”):可以当简单的输出使用,相当于sout;
- logger.debug(“message”):可以在debug时当简单的输出使用,相当于sout;
- logger.error(“message”):可以在处理异常时当简单的输出使用,相当于sout;