1. 基本概念
概念: mybatis是一个底层封装了JDBC的持久层的开源框架,前身是apache的ibatis项目,10年迁移到了google-code并改名为mybatis,13年11月迁移到github:
- ORM:Object Relation Mapping 希望通过操作java对象的方式来操作数据库:
class-name对应table-name。field-name对应column-name。field-type对应column-type。instance对应record。
- 特点:
- mybatis是半封装ORM框架,封装了驱动,连接,statement等业务代码,但不封装SQL语句。
- mybatis支持注解或XML方式单独开发SQL语句,与java代码解耦,方便后期优化。
- mybatis可以将java对象映射到SQL中,也可以将SQL结果映射成java对象。
- vs hibernate:
- mybatis半封装ORM框架,hibernate全封装ORM框架。
- mybatis开发周期长,hibernate开发周期短。
- mybatis数据库相关,切换数据库时需要重构SQL与,hibernate数据库无关。
- mybatis适合高QPS系统,如互联网软件,企业运营,电商,P2P等,hibernate适合低QPS系统,如oa办公自动化,erp企业流程系统,某些政府项目等。
框架没有最好,只有最合适。
2. 核心流程
概念:
SqlSessionFactoryBuilder:用于创建SqlSessionFactory对象的类:- 创建
SqlSessionFactory时需以读取核心配置文件。 - 核心配置文件用于配置mybatis的环境信息和SQL配置文件路径。
- SQL配置文件用于配置CRUD语句块和SQL语句相关配置信息。
- 创建
SqlSessionFactory:用于获取SqlSession对象的接口。SqlSession:用于获取数据库连接的接口。Executor:SqlSession中的SQL执行者,负责SQL执行,有基本和缓存两种执行器实现。MappedStatement:Executor中的SQL管理者,负责SQL组装和结果转换:- 每个SQL块都通过id属性对应一个SQL管理者对象。
- 输入映射:在执行SQL前,将输入的java对象参数映射至SQL中。
- 输出映射:在执行SQL后,将SQL结果映射到指定的java对象中。
3. 基本配通
概念: mybatis下载地址
- 开发数据库:创建数据库,用户,和数据库表。
- 添加项目依赖:
mybatis:mybatis核心依赖。log4j-core:log for java日志接口包。log4j:log for java日志实现包。slf4j-api:simple logging facade for java日志接口包。slf4j-log4j12:slf和log4j的整合包(一个实现)。mysql-connector-java:mysql驱动包,oracle需要导入ojdbc6.jar。lombok:pojo工具。junit:单元测试。
- 开发核心配置文件:
<configuration:核心配置文件根标签。<environments>:环境根标签,使用default属性配置默认环境。<environment>:环境标签,多环境使用id进行区分。<transactionManager>:用于配置该环境下的事务管理类。<dataSource>:用于配置该环境的数据源信息,使用type属性来指定连接池类。
- 开发日志文件:
- mybatis默认使用log4j日志,自动读取
classpath:log4j.properties日志文件。
- mybatis默认使用log4j日志,自动读取
- 测试:
Resources.getResourceAsStream():获取核心配置文件的字节流对象。Resources.getResourceAsReader():获取核心配置文件的字符流对象。new SqlSessionFactoryBuilder():创建一个会话工厂的builder对象。builder.build():根据核心配置文件创建会话工厂。factory.openSession():从会话工厂中打开一个具有事务保护的会话。factory.openSession(true):从会话工厂中打开一个不具有事务保护的会话。session.getConnection().isClosed():通过会话获取一个连接并测试状态。session.close():关闭会话,可由try-with-resource异常结构完成。
源码: /mybatis3/
- res:
README.md
## database
- cmd: `mysql -uroot -p`
- cmd: `root`
- cmd: `create database mybatis character set utf8mb4;`
- cmd: `use mybatis;`
- cmd: `grant all on mybatis.* to 'mybatis'@'localhost' identified by 'mybatis';`
- cmd: `flush privileges;`
- res:
mybatis.sql
CREATE TABLE IF NOT EXISTS `student`
(
`id` INT(11) AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(50) NOT NULL COMMENT '学生姓名',
`gender` TINYINT(1) NULL COMMENT '学生性别',
`age` INT(3) NULL COMMENT '学生年龄',
`info` VARCHAR(500) NULL COMMENT '学生信息'
)
COMMENT '学生表';
CREATE TABLE IF NOT EXISTS `teacher`
(
`id` INT(11) AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(50) NOT NULL COMMENT '教师姓名',
`gender` TINYINT(1) NULL COMMENT '教师性别',
`age` INT(3) NULL COMMENT '教师年龄',
`info` VARCHAR(500) NULL COMMENT '教师信息'
)
COMMENT '教师表';
CREATE TABLE IF NOT EXISTS `worker`
(
`id` INT(11) AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(50) NOT NULL COMMENT '工人姓名',
`gender` TINYINT(1) NULL COMMENT '工人性别',
`age` INT(3) NULL COMMENT '工人年龄',
`info` VARCHAR(500) NULL COMMENT '工人信息'
)
COMMENT '工人表';
- res:
pom.xml
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.6</version>
</dependency>
<!--log4j-core-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.3</version>
</dependency>
<!--log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--slf4j-api-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<!--slf4j-log4j12-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
<scope>test</scope>
</dependency>
<!--mysql-connector-java-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.15</version>
<scope>runtime</scope>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
<optional>true</optional>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--ant-->
<!--
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
<version>1.9.6</version>
</dependency>
-->
<!--asm-->
<!--
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>5.2</version>
</dependency>
-->
<!--cglib-->
<!--
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.5</version>
</dependency>-->
<!--commons-logging-->
<!--
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
-->
<!--ognl-->
<!--
<dependency>
<groupId>ognl</groupId>
<artifactId>ognl</artifactId>
<version>3.1.16</version>
</dependency>-->
- res:
classpath:mybatis-start.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.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC"/>
<property name="username" value="mybatis"/>
<property name="password" value="mybatis"/>
</dataSource>
</environment>
</environments>
</configuration>
- res:
classpath:log4j.propeties
log4j.rootLogger=DEBUG, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
- tst:
c.y.start.StartTest.start()
package com.yap.start;
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 org.junit.Test;
import java.io.IOException;
import java.sql.SQLException;
/**
* @author yap
*/
public class StartTest {
@Test
public void start() throws IOException {
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(
Resources.getResourceAsStream("mybatis-start.xml"));
try (SqlSession session = sessionFactory.openSession()) {
System.out.println(session.getConnection().isClosed());
} catch (SQLException e) {
e.printStackTrace();
}
}
}
3.1 事务管理类
概念: <transactionManager> 可以通过 type 来配置两种内置的事务管理类,对应别名如下:
JDBC:简单的使用了JDBC的提交和回滚,它依赖于从数据源得到的连接来管理事务。MANAGED:不会提交和回滚连接,依赖于第三方容器,如spring来对事务进行管理。
3.2 数据源类
概念: <dataSource> 可以通过 type 来配置三种内置的数据源类,对应别名如下:
UNPOOLED:未使用连接池,每次获取都是一个新连接,速度比较慢,有五种属性:driver:驱动串,如com.mysql.cj.jdbc.Driver。url:连接串,如jdbc:mysql://127.0.0.1:3306/mybatis?serverTimezone=UTC。username:连库账号,如mybatis。password:连库密码,如mybatis。defaultTransactionIsolationLevel:默认连接的事务隔离级别。
POOLED:使用连接池,比较流行的一种,除了上面五种属性外,还有以下几种额外属性:poolMaximumActiveConnections:最大活跃连接数,默认10。poolMaximumIdleConnections:最大空闲连接数。poolMaximumCheckoutTime:在被强制返回之前,池中连接被检查的时间,默认为20000毫秒。poolTimeToWait:一个连接的最长等待时间,默认20000毫秒。poolPingQuery:发送到数据库的一条侦测SQL,验证连接是否存活,默认NO PING QUERY SET。poolPingEnabled:是否开启侦测查询,若开启则必须配置poolPingQuery的值,默认false。poolPingConnectionsNotUsedFor:每隔多少秒发送一次侦测SQL,默认为0,表示每一刻都侦测。
JNDI:这个数据源的实现是为了使用如spring这样的容器,有两个属性:inital_context:这个属性用来从上下文中寻找环境。data_source:引用数据源实例的实际位置。
4. 工具类封装
概念:
- 核心对象生命周期:
SqlSessionFactoryBuilder:创建完会话工厂实例后即可销毁,建议设置为局部变量。SqlSessionFactory:可以重复使用,但不该被重复创建,建议以单例模式来管理。SqlSession:每个线程都该独享一个会话实例,建议设置为局部变量。
- 会话工厂创建方式:
build(InputStream is)build(InputStream is, String env)build(InputStream is, Properties prop)build(InputStream is, String env, Properties prop)
源码: /mybatis3/
- res:
jdbc/db.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.username=mybatis
jdbc.password=mybatis
jdbc.url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
- res:
mybatis-util.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.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC"/>
<property name="username" value="mybatis"/>
<property name="password" value="mybatis"/>
</dataSource>
</environment>
<environment id="test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
</configuration>
- src:
c.y.util.yap
package com.yap.util;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.util.Properties;
/**
* @author yap
*/
public class MyBatisUtil {
private static SqlSessionFactory factory;
public static SqlSessionFactory getFactory(String resource, String env, Properties prop) {
try {
if (factory == null) {
synchronized (MyBatisUtil.class) {
if (factory == null) {
factory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream(resource), env, prop);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return factory;
}
public static SqlSessionFactory getFactory(String resource) {
return getFactory(resource, null, null);
}
public static SqlSessionFactory getFactory(String resource, String env) {
return getFactory(resource, env, null);
}
public static SqlSessionFactory getFactory(String resource, Properties prop) {
return getFactory(resource, null, prop);
}
}
- tst:
c.y.start.MyBatisUtilTest.onlyRes()
package com.yap.start;
import com.yap.util.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.Test;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
/**
* @author yap
*/
public class MyBatisUtilTest {
@Test
public void onlyRes() {
SqlSessionFactory factory = MyBatisUtil.getFactory("mybatis-start.xml");
System.out.println(MyBatisUtil.getFactory("mybatis-start.xml").hashCode());
System.out.println(MyBatisUtil.getFactory("mybatis-start.xml").hashCode());
try (SqlSession session = factory.openSession()) {
System.out.println(session.getConnection().isClosed());
} catch (Exception e) {
e.printStackTrace();
}
}
}
- tst:
c.y.start.MyBatisUtilTest.resAndEnv()
@Test
public void resAndEnv() {
SqlSessionFactory factory = MyBatisUtil.getFactory("mybatis-start.xml", "test");
try (SqlSession session = factory.openSession()) {
System.out.println(session.getConnection().isClosed());
} catch (Exception e) {
e.printStackTrace();
}
}
- tst:
c.y.start.MyBatisUtilTest.resAndProps()
@Test
public void resAndProps() throws IOException {
Properties props = new Properties();
props.load(new FileInputStream("src/main/resources/jdbc/db.properties"));
SqlSessionFactory factory = MyBatisUtil.getFactory("mybatis-start.xml", props);
try (SqlSession session = factory.openSession()) {
System.out.println(session.getConnection().isClosed());
} catch (Exception e) {
e.printStackTrace();
}
}
- tst:
c.y.start.MyBatisUtilTest.resAndEnvAndProps()
@Test
public void resAndEnvAndProps() throws IOException {
Properties props = new Properties();
props.load(new FileInputStream("src/main/resources/jdbc/db.properties"));
SqlSessionFactory factory = MyBatisUtil.getFactory("mybatis-start.xml", "test", props);
try (SqlSession session = factory.openSession()) {
System.out.println(session.getConnection().isClosed());
} catch (Exception e) {
e.printStackTrace();
}
}
5. 属性文件
概念: 除了在在创建会话工厂时引入属性文件外,还可以在核心配置文件中使用 <properties resource=""> 进行引入。
源码: /mybatis3/
- res:
classpath:mybatis-properties.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>
<properties resource="jdbc/db.properties"/>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/student-mapper.xml"/>
</mappers>
</configuration>
- tst:
c.y.start.MyBatisUtilTest.refPropsByXml()
@Test
public void refPropsByXml() {
SqlSessionFactory factory = MyBatisUtil.getFactory("mybatis-properties.xml");
try (SqlSession session = factory.openSession()) {
System.out.println(session.getConnection().isClosed());
} catch (Exception e) {
e.printStackTrace();
}
}