这是基于b站狂神的JavaWeb课程的笔记 课程链接
1. 简介
1.1 什么是MyBatis?
- MyBatis 是一款优秀的持久层框架
- MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作
- MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO为数据库中的记录
1.2 持久化
- 将程序的数据在瞬时状态和持久状态转化的过程
- JDBC、io文件持久化
- 为什么要持久化:
- 内存特点:断电即失
- 内存太贵了...
1.3 持久层
(dao层,service层,controller层...)
- 完成持久化工作的代码块
- 层界限十分明显
1.4 为什么需要MyBatis?
- 方便(传统JDBC代码笨重)
- 流行
- 优点:
- sql和代码分离
- orm映射(对象与数据库)
- xml标签,支持动态sql
2. Hello, MyBatis
思路:搭建环境 导入MyBatis 编写代码 测试!
2.1 环境搭建
建一个新的数据库
create database `mybatis`;
use `mybatis`;
create table `user` (
`id` int(20) not null primary key,
`name` varchar(30) default null,
`pwd` varchar(30) default null
) engine=innodb default charset=utf8;
insert into `user` values (1, '张三','123456');
insert into `user` values (2, '李四','112233');
insert into `user` values (3, 'yldog','003773')
新建一个普通的Maven项目
导入依赖 (配置pom.xml)
<dependencies>
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!-- 添加mysql驱动依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.8</version>
</dependency>
<!-- Junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
在刚才搭建的maven里新建Module -- 为maven的子工程
- 在父工程的pom.xml里有
<modules>
<module>mybatis-01-hello</module>
</modules>
- 在子工程的pom.xml里有
<parent>
<artifactId>mybatis-learn</artifactId>
<groupId>com.yldog</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
- 好处:父工程中的依赖自动进入子工程,无需重复加入依赖
2.2 创建编写一个模块
编写mybatis核心配置文件
- (一般)在resource目录下创建mybatis-config.xml文件
- 编写配置 -- 这个操作等同于之前的读取properties.db
<?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"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>
编写MyBatis工具类
每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。
// 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();
}
}
// 由sqlSessionFactory获得sqlSession的实例
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession();
}
}
2.3 编写代码
- 实体类
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 + '\'' +
'}';
}
}
- 接口类
- 写一个接口类,拿到所有用户
public interface UserMapper {
List<User> getUserList();
}
- 实现接口由之前的编写一个UserMapperImpl类变为编写和绑定一个Mapper的xml配置文件
<!--namespace 绑定了一个mapper接口-->
<!--select 是一个查询语句-->
<!--id 指定了接口的方法-->
<!--resultType对应之前的结果集ResultSet-->
<mapper namespace="com.yldog.dao.UserMapper" >
<select id="getUserList" resultType="com.yldog.pojo.User">
select * from mybatis.user
</select>
</mapper>
注意事项
- (*)mapper 需在mybatis-config.xml中进行注册!
<!-- 每一个Mapper.xml都需要在mybatis核心配置文件中注册! -->
<mappers>
<mapper resource="com/yldog/dao/UserMapper.xml"/>
</mappers>
- Maven出现的资源过滤问题,需在pom.xml中加入如下配置 (将xml后缀的文件也一起导出):
<!--maven的资源过滤问题-->
<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>
2.4 测试 (用法)
@Test
public void getUserListTest() {
// 获得sqlSession
SqlSession sqlSession = MybatisUtils.getSqlSession();
// 执行sql
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 执行方法,拿到结果
for (User user : userMapper.getUserList()) {
System.out.println(user.toString());
}
// 关闭sqlSession
sqlSession.close();
}
3.CRUD
1.编写接口
2.在接口的xml文件中绑定接口,并编写sql
3.测试
一个用户管理接口:
public interface UserMapper {
// 获取所有用户
List<User> getUserList();
// 根据id查询用户
User getUserById(int id);
// 增加新用户
int insert(User user);
// 更新用户
int update(User user);
// 根据id删除用户
int deleteById(int id);
}
在这个接口的xml配置文件中:
<mapper namespace="com.yldog.dao.UserMapper" >
<select id="getUserList" resultType="com.yldog.pojo.User">
select * from mybatis.user;
</select>
<select id="getUserById" parameterType="int" resultType="com.yldog.pojo.User">
select * from mybatis.user where id = #{id};
</select>
<insert id="insert" parameterType="com.yldog.pojo.User" >
insert into mybatis.user (id,name,pwd) values (#{id},#{name},#{pwd});
</insert>
<update id="update" parameterType="com.yldog.pojo.User" >
update mybatis.user set name=#{name},pwd=#{pwd} where id=#{id};
</update>
<delete id="deleteById" parameterType="int">
delete from mybatis.user where id = #{id};
</delete>
</mapper>
3.1. namespace
- 绑定了这个接口
<mapper namespace="com.yldog.dao.UserMapper" >
...
</mapper>
3.2. select
<select id="getUserList" resultType="com.yldog.pojo.User">
select * from mybatis.user;
</select>
- id: 绑定了接口中要实现的方法
- resultType:方法的返回值
- paramType:方法的参数
测试
@Test
public void getUserListTest() {
// 获得sqlSession
SqlSession sqlSession = MybatisUtils.getSqlSession();
// 执行sql
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
// 执行方法,拿到结果
for (User user : userMapper.getUserList()) {
System.out.println(user.toString());
}
// 关闭sqlSession
sqlSession.close();
}
3.3. insert
注意:
- 对象中的属性,可以直接取出来 i.e. (id, name, pwd)
- 方法中的参数放在 #{} 中
<insert id="insert" parameterType="com.yldog.pojo.User" >
insert into mybatis.user (id,name,pwd) values (#{id},#{name},#{pwd});
</insert>
测试
注意:要使用 sqlSession.commit()
提交事务,如同在JDBC中一样
@Test
public void insertTest() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
int res = userMapper.insert(new User(5,"zzh","000001"));
// 提交事务,否则无法更改
sqlSession.commit();
sqlSession.close();
}
3.4. update
<update id="update" parameterType="com.yldog.pojo.User" >
update mybatis.user set name=#{name},pwd=#{pwd} where id=#{id};
</update>
测试
@Test
public void updateTest() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
userMapper.update(new User(5, "zzh", "000002"));
sqlSession.commit();
sqlSession.close();
}
3.5. delete
<delete id="deleteById" parameterType="int">
delete from mybatis.user where id = #{id};
</delete>
测试
@Test
public void deleteByIdTest() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
userMapper.deleteById(5);
sqlSession.commit();
sqlSession.close();
}
3.6. 万能的map
如果实体类或者数据库中的表,参数或者字段过多,应当考虑使用Map!
- 接口
// 用map更新
int updateByMap(Map map);
- xml
<update id="updateByMap" parameterType="map">
update mybatis.user set name=#{name}, pwd=#{pwd} where id=#{id};
</update>
- 测试
@Test
public void updateByMapTest() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
Map<String, Object> map = new HashMap<>();
map.put("name", "kdb");
map.put("pwd", "123123");
map.put("id", 5);
userMapper.updateByMap(map);
sqlSession.commit();
sqlSession.close();
}
3.7. 模糊查询
- 接口
// 根据name模糊查询用户
List<User> getUserByName(String value);
- xml
<select id="getUserByName" parameterType="String" resultType="com.yldog.pojo.User">
select * from mybatis.user where name like #{value}
</select>
- 测试
在输入参数时用%拼接"%value%"
@Test
public void getUserByNameTest() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
for (User user : userMapper.getUserByName("%李%")) {
System.out.println(user);
}
sqlSession.close();
}
4.配置解析
4.1.核心配置文件
- mybatis-config.xml
4.2.环境配置(environments)
<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="003773"/>
</dataSource>
</environment>
</environments>
- envrionments中可以配置多套环境,使用
default="环境名"
来选择使用哪一套 - transactionManager (事务管理器)
- 有两种选择
"JDBC"
和"MANAGED"
,一般选择JDBC - 使用 Spring + MyBatis,则没有必要配置事务管理器
- 有两种选择
- dataSource(数据源)
type="[UNPOOLED|POOLED|JNDI]"
- 这里使用
"POOLED"
, 优点:
这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,能使并发 Web 应用快速响应请求。
4.3. 属性(properties)
- 方式1:在外部进行配置,并可以进行动态替换
- 编写一个db.properties配置文件
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8 username=root password=003773
- 在mybatis-config中引入
dataSource下使用<!-- 引入外部文件配置 --> <properties resource="db.properties"/>
${}
提取替换<dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource>
- 方式2:直接在dataSource下编写
<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="003773"/>
</dataSource>
- 方式3:在properties下编写(可同时引入外部文件)
<!-- 引入外部文件配置 -->
<properties resource="db.properties">
<property name="username" value="xxxxxx"/>
<property name="password" value="xxxxxx"/>
</properties>
注意:若外部配置文件与 <property>
中 name
一样,则程序会优先考虑外部配置文件中的值
4.4. 类型别名(typeAliases)
- 方式1:
指定一个类
<typeAliases>
<typeAlias type="com.yldog.pojo.User" alias="User"/>
</typeAliases>
- 方式2:
扫描整个包,为包内所有实体类起别名
<typeAliases>
<package name="com.yldog.pojo"/>
</typeAliases>
默认的别名为:
Bean 的首字母小写的非限定类名来作为它的别名
e.g. User
user
若有注解,则别名为其注解值
@Alias("otherAliasForUser")
public class User {
...
}
4.5. 映射器(mappers)
我们需要告诉 MyBatis 到哪里去找定义 好的SQL 映射语句
- 方法1(推荐): 使用相对于类路径的资源引用
<!-- 每一个Mapper.xml都需要在mybatis核心配置文件中注册! -->
<mappers>
<mapper resource="com/yldog/dao/UserMapper.xml"/>
</mappers>
- 方法2:使用映射器接口实现类的完全限定类名
<mappers>
<mapper class="com.yldog.dao.UserMapper"/>
</mappers>
- 方法3:扫描包,将包内的映射器接口实现全部注册为映射器
<mappers>
<package name="com.yldog.dao"/>
</mappers>
注意:方式2,3需要:
- 接口和它的Mapper配置文件必须同名!
-
接口和它的Mapper配置文件必须在一个包下!
e.g.
4.6. 生命周期和作用域
错误的使用会导致非常严重的并发问题。
- SqlSessionFactoryBuilder
- 一旦创建了 SqlSessionFactory,就不再需要它了
- 局部方法变量
- SqlSessionFactory
- 可以想象成:数据库连接池
- SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例
- SqlSessionFactory 的最佳作用域是应用作用域
- 单例模式或者静态单例模式
- SqlSession
- 连接到连接池的一个请求!
- SqlSession 的实例不是线程安全的,因此是不能被共享的
- 最佳的作用域是请求或方法作用域
- 用完要关闭!
- 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中
5. 解决属性名和字段名不一致的问题(ResultMap)
- 问题:比如,User实体类中属性为
private String password;
,而数据库表格中字段名称为pwd
使用原本的mapper xml文件来执行查询语句,会出现这样的问题:
在数据库查出pwd
字段后无法被赋值到user.password
上因为名称不同。 - 解决:使用一个简单的resultMap
编写一个结果映射集
<!-- 结果集映射 -->
<!-- type 为实体类 -->
<resultMap id="userResultMap" type="User">
<!-- 名称一样的自动(隐式)映射 -->
<!-- <result column="id" property="id"/> -->
<!-- <result column="name" property="name"/> -->
<!-- column为表格字段
property为类属性 -->
<result column="pwd" property="password"/>
</resultMap>
在Mapper文件中使用resultMap
,指定这个结果映射集
<!-- 将resultType 变成 resultMap -->
<select id="getUserById" parameterType="int" resultMap="userResultMap">
select * from mybatis.user where id = #{id};
</select>
-
结果:成功赋值
6. 日志
- MyBatis中日志的使用可在mybatis-config.xml中的
settings
中配置 - 设置名为
logImpl
- 有以下种类 SLF4J |
LOG4J
| LOG4J2 | JDK_LOGGING | COMMONS_LOGGING |STDOUT_LOGGING
| NO_LOGGING - 默认为无日志
6.1 使用Log4j日志
6.1.1 什么是Log4j?
- 通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器等
- 可以控制每一条日志的输出格式
- 定义每一条日志信息的级别
- 可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码
6.1.2. 导入依赖
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
6.1.3. 配置mybatis-config.xml
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
6.1.4. resources目录下加入Log4j的配置文件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/yldog.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
6.1.5. 使用
执行以下sql:
@Test
public void getUserByIdTest() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
System.out.println("id:1 --> " + userMapper.getUserById(1).toString());
sqlSession.close();
}
结果:也可以自主的输出信息,决定输出信息的级别:
- 构建日志对象,参数为当前类的class
static Logger logger = Logger.getLogger(UserMapper.class);
- 输出不同级别的信息
@Test
public void log4jTest() {
logger.info("info:进入了log4jTest");
logger.debug("debug:进入了log4jTest");
logger.error("error:进入了log4jTest");
}
- 结果
[com.yldog.dao.UserMapper]-info:进入了log4jTest
[com.yldog.dao.UserMapper]-debug:进入了log4jTest
[com.yldog.dao.UserMapper]-error:进入了log4jTest
6.2. 使用 STDOUT_LOGGING
配置mybatis-config.xml即可
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
7. 使用Limit分页
- 编写接口方法使,用Map作为参数
List<User> getUserByLimit(Map<String, Integer> map);
- 编写Mapper.xml
<select id="getUserByLimit" parameterType="map" resultMap="userResultMap">
select * from mybatis.user limit #{startIndex}, #{pageSize};
</select>
这样就能从输入的Map中提取 startIndex
和 pageSize
- 测试
@Test
public void getUserByLimitTest() {
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String, Integer> map = new HashMap<>();
// 从第一条记录开始查2条记录
map.put("startIndex", 0);
map.put("pageSize", 2);
for (User user : mapper.getUserByLimit(map)) {
System.out.println(user);
}
sqlSession.close();
}
8. 使用注解开发
- 不需要Mapper.xml配置
- 需要在mybatis-config.xml中绑定接口
<!-- 注解需要绑定接口 -->
<mappers>
<mapper class="com.yldog.dao.UserMapper"/>
</mappers>
实例1 - 根据id查找用户:
// 根据id查询用户
@Select("select * from user where id = #{id}")
User getUserById(@Param("id") int id);
注意
- 在方法的参数前,若是基本类型或String类,在需要加上
@Param("参数")
,这个参数sql语句中#{参数}
所提取的参数 - 结果:
User{id=1, name='张三', pwd='null'}
- 由于属性名和字段名不一致,
pwd
为null
,且这里使用注解无法像之前那样,在Mapper.xml中配置进行属性和字段的映射,所以注解适用于比较简单的sql语句,有局限性
实例2 - 新增用户
@Insert("insert into user(id,name,pwd) values (#{id}, #{name}, #{password}) ")
int insertUser(User user);
- 注意这里方法的参数为引用类型,故前面不用加
@Param()
- 以此类推,
- 更新 注解为
@Update("sql")
- 删除 注解为
@Delete("sql")
- 更新 注解为
笔记第二部分链接:MyBatis 入门笔记+理解(二)