目录
思路流程:搭建环境-->导入Mybatis--->编写代码--->测试
-
Mybatis官方文档 : http://www.mybatis.org/mybatis-3/zh/index.html
-
GitHub : https://github.com/mybatis/mybatis-3
思路流程:搭建环境-->导入Mybatis--->编写代码--->测试
1、搭建实验数据库
CREATE DATABASE `mybatis`;
USE `mybatis`;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`user_id` int(20) NOT NULL,
`user_name` varchar(30) DEFAULT NULL,
`user_pwd` varchar(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into `user`(`id`,`name`,`pwd`) values (1,'李四','123456'),(2,'张三','abcdef'),(3,'李四','987654');
2、导入MyBatis相关 jar 包
<?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>org.example</groupId>
<artifactId>com.yc</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>mybatis-01</module>
<module>mybatis-02</module>
<module>mybatis-02</module>
<module>mybatis-03</module>
<module>mybatis-04</module>
<module>mybatis-05</module>
<module>mybatis-06</module>
<module>mybatis-07</module>
<module>mybatis-08</module>
<module>mybatis-11</module>
<module>mybatis-09</module>
<module>mybatis-10</module>
</modules>
<!--导入依赖-->
<dependencies>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.15</version>
</dependency><!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<!--mybatis驱动-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<!--junit测试@Test-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
<!--在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>
</project>
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核心配置文件-->
<configuration>
<!--引入外部配置文件-->
<properties resource="db.properties">
<property name="username" value="root"/><!--直接引入外部文件可以在其中增加一些配置属性-->
<property name="password" value="password"/><!--如果两个文件有同一个字段,优先使用外部配置文件-->
</properties>
<!--
在实体类比较少的时候,使用第一种方式typeAliases.
如果实体类十分多,建议使用第二种package.
第一种可以DIY(自定义别名),第二种则不行
如果非要修改别名,需要在实体上增加注解。
-->
<!--可以给实体类起别名-->
<!-- <typeAliases>-->
<!-- <typeAlias type="com.kuang.pojo.User" alias="User"/>-->
<!-- </typeAliases>-->
<!-- 也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:扫描实体类的包,它的默认别名就为这个类的类名,!!!首字母小写!-->
<typeAliases>
<package name="com.kuang.pojo"/>
</typeAliases>
<environments default="mysql"><!--默认环境-->
<environment id="mysql">
<transactionManager type="JDBC"/>
<!--
事务管理器 jdbc这个配置支持提交和回滚,它依赖从数据源得到的连接来管理事务的作用域
MANAGE不支持提交和回滚
-->
<dataSource type="POOLED"><!--数据源链接数据库dbcp c3p0 druid-->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!--每一个Mapper.XML都需要在Mybatis核心配置文件中注册-->
<mappers>
<!-- 使用相对于类路径的资源引用 -->
<!-- <mapper resource="com/kuang/dao/UserDao.xml"/>-->
<!-- 将包内的映射器接口实现全部注册为映射器
1.注意在使用package 和class时 接口和他的Mapper配置文件必须同名
2.接口和它的Mapper配置文件必须在同一个包下!
-->
<!--<package name="com.kuang.dao"/>-->
<!-- 使用映射器接口实现类的完全限定类名 使用class文件绑定注册-->
<mapper class="com.kuang.dao.UserDao"/>
</mappers>
</configuration>
4、编写MyBatis工具类
package com.kuang.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;
//SqlSessionFactory-->>sqlSession
/*
qlSessionFactoryBuilder
生命周期,和作用域,是至关重要的,因为错误的使用会导致非常严重的并发问题。
一旦创建了SqlSessionFactoryBuilder,就不再需要它了
所以要将SqlSessionFactoryBuilder设置为局部变量
SqlSessionFactory 就是可以想象为:数据库连接池 connection
SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。
因此 SqlSessionFactory 的最佳作用域是应用作用域。
最简单的就是使用单例模式或者静态单例模式。
SqlSession
每个线程都应该有它自己的 SqlSession 实例。(连接到连接池的一个请求)所以需要关闭
SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。(用完之后需要赶紧关闭,否则资源被占用)
*/
/* 开始 mybatis-config.xml配置文件---->SqlSessionFactoryBuilder----->SqlSessionFactory---->SqlSession---->SQL Mapper--->结束*/
public class MybatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static{
//使用mybatis第一步获取sqlSessionFactory对象
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/*
既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。
SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。
*/
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
5、创建实体类
package com.kuang.pojo;
import org.apache.ibatis.type.Alias;
import java.io.Serializable;
//实体类
@Alias("user")
public class User implements Serializable {
private int user_id;
private String user_name;
private String user_pwd;
public User() {
}
public User(int user_id, String user_name, String user_pwd) {
this.user_id = user_id;
this.user_name = user_name;
this.user_pwd = user_pwd;
}
public int getUser_id() {
return user_id;
}
public void setUser_id(int user_id) {
this.user_id = user_id;
}
public String getUser_name() {
return user_name;
}
public void setUser_name(String user_name) {
this.user_name = user_name;
}
public String getUser_pwd() {
return user_pwd;
}
public void setUser_pwd(String user_pwd) {
this.user_pwd = user_pwd;
}
@Override
public String toString() {
return "User{" +
"user_id=" + user_id +
", user_name='" + user_name + '\'' +
", user_pwd='" + user_pwd + '\'' +
'}';
}
}
6、编写Mapper接口类
package com.kuang.dao;
import com.kuang.pojo.User;
import java.util.List;
import java.util.Map;
public interface UserDao {
//查询全部用户
List<User> getUserList();
//根据ID查询用户
User getUserById(int id);
//insert 插入一个用户
int addUser(User user);
//修改用户
int updateUser(User user);
//删除一个用户
int deleteUser(int id);
}
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.kuang.dao.UserDao">
<!--id对应接口的方法名 resultType返回结果 要写全限命名类型 parameterType参数类型-->
<select id="getUserLike" resultType="com.kuang.pojo.User">
select * from t_user where user_name like "%"#{value}"%"
</select>
<!--select查询语句-->
<select id="getUserList" resultType="com.kuang.pojo.User">
select * from t_user
</select>
<!--根据ID查询-->
<select id="getUserById" parameterType="int" resultType="com.kuang.pojo.User">
select * from t_user where user_id = #{UserId}
</select>
<!--对象中的属性,可以直接取出来-->
<insert id="addUser" parameterType ="com.kuang.pojo.User">
insert into t_user(user_id,user_name,user_pwd) value (#{user_id},#{user_name},#{user_pwd});
</insert>
<!--map执行-->
<insert id="addUser2" parameterType ="map">
insert into t_user(user_id,user_name,user_pwd) value (#{user_id},#{user_name},#{user_pwd})
</insert>
<update id="updateUser" parameterType="com.kuang.pojo.User">
update t_user
set user_name=#{user_name},user_pwd=#{user_pwd}
where user_id=#{user_id};
</update>
<delete id="deleteUser" parameterType="int">
delete from t_user where user_id=#{user_id}
</delete>
</mapper>
8、编写测试类
package com.kuang.dao;
import com.kuang.pojo.User;
import com.kuang.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public class UserDaoTest {
//模糊查询注意通配符的使用% %
@Test
public void getUserLike(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
List<User> userLike = mapper.getUserLike("李");
for (User user : userLike) {
System.out.println(user);
}
sqlSession.close();
}
@Test
public void test(){
//第一步:获得SQLSession对象
SqlSession sqlSession = MybatisUtils.getSqlSession();
try {
//方式一:getMapper执行SQL
UserDao userDao= sqlSession.getMapper(UserDao.class);
List<User> userList = userDao.getUserList();
//第二种方法
// List<User> userList = sqlSession.selectList("com.kuang.dao.UserDao.getUserList");
for (User user : userList) {
System.out.println(user);
}
}catch(Exception e){
e.printStackTrace();
}finally {
//关闭SqlSession
sqlSession.close();
}
}
@Test
public void getUserById(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
try{
UserDao mapper = sqlSession.getMapper(UserDao.class);
User userById = mapper.getUserById(3);
System.out.println(userById);
}
catch(Exception e){
e.printStackTrace();
}finally{
sqlSession.close();
}
}
//增删该需要提交事务
@Test
public void addUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
try {
UserDao mapper = sqlSession.getMapper(UserDao.class);
int res = mapper.addUser(new User(4, "哈哈", "1233333"));
if(res>0){
System.out.println("插入成功!");
}
//提交事务
sqlSession.commit();
}catch(Exception e){
e.printStackTrace();
}finally{
sqlSession.close();
}
}
@Test
public void addUser2(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
Map<String, Object> map = new HashMap<String,Object>();
map.put("user_id",7);
map.put("user_name","111");
map.put("user_pwd","222333");
mapper.addUser2(map);
sqlSession.commit();
sqlSession.close();
}
@Test
public void updateUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
try {
UserDao mapper = sqlSession.getMapper(UserDao.class);
mapper.updateUser(new User(4,"王五","123123"));
sqlSession.commit();
}catch(Exception e){
e.printStackTrace();
}finally {
sqlSession.close();
}
}
@Test
public void deleteUser(){
SqlSession sqlSession = MybatisUtils.getSqlSession();
try {
UserDao mapper = sqlSession.getMapper(UserDao.class);
mapper.deleteUser(6);
sqlSession.commit();
}catch(Exception e){
e.printStackTrace();
}finally {
sqlSession.close();
}
}
}
配置解析
-
mybatis-config.xml 系统核心配置文件
-
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。
-
能配置的内容如下
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
<!-- 注意元素节点的顺序!顺序不对会报错 -->
environments元素
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="..." value="..."/>
</transactionManager>
<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>
</environments>
-
配置MyBatis的多套运行环境,将SQL映射到多个不同的数据库上,必须指定其中一个为默认运行环境(通过default指定)
-
子元素节点:environment
-
dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。
-
数据源是必须配置的。
-
有三种内建的数据源类型
type="[UNPOOLED|POOLED|JNDI]")
-
unpooled:这个数据源的实现只是每次被请求时打开和关闭连接。
-
pooled:这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来 , 这是一种使得并发 Web 应用快速响应请求的流行处理方式。
-
jndi:这个数据源的实现是为了能在如 Spring 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。
-
数据源也有很多第三方的实现,比如dbcp,c3p0,druid等等....
-
详情:点击查看官方文档
-
这两种事务管理器类型都不需要设置任何属性。
-
具体的一套环境,通过设置id进行区别,id保证唯一!
-
子元素节点:transactionManager - [ 事务管理器 ]
<!-- 语法 --> <transactionManager type="[ JDBC | MANAGED ]"/>
-
- 子元素节点:数据源(dataSource)
mappers
-
映射器 : 定义映射SQL语句文件
-
既然 MyBatis 的行为其他元素已经配置完了,我们现在就要定义 SQL 映射语句了。但是首先我们需要告诉 MyBatis 到哪里去找到这些语句。Java 在自动查找这方面没有提供一个很好的方法,所以最佳的方式是告诉 MyBatis 到哪里去找映射文件。你可以使用相对于类路径的资源引用, 或完全限定资源定位符(包括
file:///
的 URL),或类名和包名等。映射器是MyBatis中最核心的组件之一,在MyBatis 3之前,只支持xml映射器,即:所有的SQL语句都必须在xml文件中配置。而从MyBatis 3开始,还支持接口映射器,这种映射器方式允许以Java代码的方式注解定义SQL语句,非常简洁。
引入资源方式
!-- 使用相对于类路径的资源引用 -->
<mappers>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- 使用完全限定资源定位符(URL) -->
<mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
</mappers>
<!--
使用映射器接口实现类的完全限定类名
需要配置文件名称和接口名称一致,并且位于同一目录下
-->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
</mappers>
<!--
将包内的映射器接口实现全部注册为映射器
但是需要配置文件名称和接口名称一致,并且位于同一目录下
-->
<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.kuang.mapper.UserMapper">
</mapper>
-
namespace中文意思:命名空间,作用如下:
-
namespace的命名必须跟某个接口同名
-
接口中的方法与映射文件中sql语句id应该一一对应
-
namespace和子元素的id联合保证唯一 , 区别不同的mapper
-
绑定DAO接口
-
namespace命名规则 : 包名+类名
-
MyBatis 的真正强大在于它的映射语句,这是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 为聚焦于 SQL 而构建,以尽可能地为你减少麻烦。
Properties优化
数据库这些属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,亦可通过 properties 元素的子元素来传递。具体的官方文档
我们来优化我们的配置文件
第一步 ; 在资源目录下新建一个db.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8
username=root
password=123456
第二步 : 将文件导入properties 配置文件
<configuration>
<!--导入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>
</environments>
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
</configuration>
typeAliases优化
类型别名是为 Java 类型设置一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。
<!--配置别名,注意顺序-->
<typeAliases>
<typeAlias type="com.kuang.pojo.User" alias="User"/>
</typeAliases>
当这样配置时,User
可以用在任何使用com.kuang.pojo.User
的地方。
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
<typeAliases>
<package name="com.kuang.pojo"/>
</typeAliases>
每一个在包 com.kuang.pojo
中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。
若有注解,则别名为其注解值。见下面的例子:
@Alias("user")
public class User {
...
}
设置
对象工厂
-
设置(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>
类型处理器
-
无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。
-
你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。【了解即可】
-
MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。
-
默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过有参构造方法来实例化。
-
如果想覆盖对象工厂的默认行为,则可以通过创建自己的对象工厂来实现。【了解即可】
作用域(Scope)和生命周期
理解我们目前已经讨论过的不同作用域和生命周期类是至关重要的,因为错误的使用会导致非常严重的并发问题。
我们可以先画一个流程图,分析一下Mybatis的执行过程!
作用域理解
-
SqlSessionFactoryBuilder 的作用在于创建 SqlSessionFactory,创建成功后,SqlSessionFactoryBuilder 就失去了作用,所以它只能存在于创建 SqlSessionFactory 的方法中,而不要让其长期存在。因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。
-
SqlSessionFactory 可以被认为是一个数据库连接池,它的作用是创建 SqlSession 接口对象。因为 MyBatis 的本质就是 Java 对数据库的操作,所以 SqlSessionFactory 的生命周期存在于整个 MyBatis 的应用之中,所以一旦创建了 SqlSessionFactory,就要长期保存它,直至不再使用 MyBatis 应用,所以可以认为 SqlSessionFactory 的生命周期就等同于 MyBatis 的应用周期。
-
由于 SqlSessionFactory 是一个对数据库的连接池,所以它占据着数据库的连接资源。如果创建多个 SqlSessionFactory,那么就存在多个数据库连接池,这样不利于对数据库资源的控制,也会导致数据库连接资源被消耗光,出现系统宕机等情况,所以尽量避免发生这样的情况。
-
因此在一般的应用中我们往往希望 SqlSessionFactory 作为一个单例,让它在应用中被共享。所以说 SqlSessionFactory 的最佳作用域是应用作用域。
-
如果说 SqlSessionFactory 相当于数据库连接池,那么 SqlSession 就相当于一个数据库连接(Connection 对象),你可以在一个事务里面执行多条 SQL,然后通过它的 commit、rollback 等方法,提交或者回滚事务。所以它应该存活在一个业务请求中,处理完整个请求后,应该关闭这条连接,让它归还给 SqlSessionFactory,否则数据库资源就很快被耗费精光,系统就会瘫痪,所以用 try...catch...finally... 语句来保证其正确关闭。
-
所以 SqlSession 的最佳的作用域是请求或方法作用域。