ORM框架的发展历史与MyBatis的高级应用

4 篇文章 1 订阅
3 篇文章 0 订阅

一、ORM框架的发展历程

1. JDBC操作

1.1 JDBC操作的特点

最初的时候我们肯定是直接通过jdbc来直接操作数据库的,本地数据库我们有一张t_user表,那么我们的操作流程是

// 注册 JDBC 驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 打开连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatisdb?
characterEncoding=utf-8&serverTimezone=UTC", "root", "123456");
// 执行查询
stmt = conn.createStatement();
String sql = "SELECT id,user_name,real_name,password,age,d_id from t_user where
id = 1";
ResultSet rs = stmt.executeQuery(sql);
// 获取结果集
while (rs.next()) {
Integer id = rs.getInt("id");
String userName = rs.getString("user_name");
String realName = rs.getString("real_name");
String password = rs.getString("password");
Integer did = rs.getInt("d_id");
user.setId(id);
user.setUserName(userName);
user.setRealName(realName);
user.setPassword(password);
user.setDId(did);
System.out.println(user);
}

具体的操作步骤是,首先在pom.xml中引入MySQL的驱动依赖,注意MySQL数据库的版本

  1. Class.forName注册驱动
  2. 获取一个Connection对象
  3. 创建一个Statement对象
  4. execute()方法执行SQL语句,获取ResultSet结果集
  5. 通过ResultSet结果集给POJO的属性赋值
  6. 最后关闭相关的资源
    这种实现方式首先给我们的感觉就是操作步骤比较繁琐,在复杂的业务场景中会更麻烦。尤其是我们需要自己来维护管理资源的连接,如果忘记了,就很可能造成数据库服务连接耗尽。同时我们还能看到具体业务的SQL语句直接在代码中写死耦合性增强。每个连接都会经历这几个步骤,重复代码很多
  7. 总结上面的操作的特点:
    1. 代码重复
    2. 资源管理
    3. 结果集处理
    4. SQL耦合

针对这些问题我们可以自己尝试解决下

1.2 JDBC优化1.0

针对常规jdbc操作的特点,我们可以先从代码重复和资源管理方面来优化,我们可以创建一个工具类来专门处理这个问题

package com.gupaoedu.crud.jdbc.v2;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class DBUtils {
private static final String JDBC_URL =
"jdbc:mysql://localhost:3306/mybatisdb?characterEncoding=utf8&serverTimezone=UTC";
private static final String JDBC_NAME = "root";
private static final String JDBC_PASSWORD = "123456";
private static Connection conn;
/**
* 对外提供获取数据库连接的方法
* @return
* @throws Exception
*/
public static Connection getConnection() throws Exception {
if(conn == null){
try{
conn =
DriverManager.getConnection(JDBC_URL,JDBC_NAME,JDBC_PASSWORD);
}catch (Exception e){
e.printStackTrace();
throw new Exception();
}
}
return conn;
}
/**
* 关闭资源
* @param conn
*/
public static void close(Connection conn ){
close(conn,null);
}
public static void close(Connection conn, Statement sts ){
close(conn,sts,null);
}
public static void close(Connection conn, Statement sts , ResultSet rs){
if(rs != null){
try {
rs.close();
}catch (Exception e){
e.printStackTrace();
}
}
if(sts != null){
try {
sts.close();
}catch (Exception e){
e.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
}

对应的jdbc操作代码可以简化如下

/**
*
* 通过JDBC查询用户信息
* */
public void queryUser(){
Connection conn = null;
Statement stmt = null;
User user = new User();
ResultSet rs = null;
try {
// 注册 JDBC 驱动
// Class.forName("com.mysql.cj.jdbc.Driver");
// 打开连接
conn = DBUtils.getConnection();
// 执行查询
stmt = conn.createStatement();
String sql = "SELECT id,user_name,real_name,password,age,d_id from
t_user where id = 1";
rs = stmt.executeQuery(sql);
// 获取结果集
while (rs.next()) {
Integer id = rs.getInt("id");
String userName = rs.getString("user_name");
String realName = rs.getString("real_name");
String password = rs.getString("password");
Integer did = rs.getInt("d_id");
user.setId(id);
user.setUserName(userName);
user.setRealName(realName);
user.setPassword(password);
user.setDId(did);
System.out.println(user);
}
} catch (SQLException se) {
se.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
DBUtils.close(conn,stmt,rs);
}
}
/**
* 通过JDBC实现添加用户信息的操作
*/
public void addUser(){
Connection conn = null;
Statement stmt = null;
try {
// 打开连接
conn = DBUtils.getConnection();
// 执行查询
stmt = conn.createStatement();
String sql = "INSERT INTO
T_USER(user_name,real_name,password,age,d_id)values('wangwu','王
五','111',22,1001)";
int i = stmt.executeUpdate(sql);
System.out.println("影响的行数:" + i);
} catch (SQLException se) {
se.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
DBUtils.close(conn,stmt);
}
}

但是整体的操作步骤还是会显得比较复杂,这时我们可以进一步优化

1.3 JDBC优化2.0

我们可以针对DML操作的方法来优化,先解决SQL耦合的问题,在DBUtils中封装DML操作的方法。

/**
* 执行数据库的DML操作
* @return
*/
public static Integer update(String sql,Object ... paramter) throws
Exception{
conn = getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
if(paramter != null && paramter.length > 0){
for (int i = 0; i < paramter.length; i++) {
ps.setObject(i+1,paramter[i]);
}
}
int i = ps.executeUpdate();
close(conn,ps);
return i;
}

然后在DML操作的时候我们就可以简化为如下步骤

/**
* 通过JDBC实现添加用户信息的操作
*/
public void addUser(){
String sql = "INSERT INTO
T_USER(user_name,real_name,password,age,d_id)values(?,?,?,?,?)";
try {
DBUtils.update(sql,"wangwu","王五","111",22,1001);
} catch (Exception e) {
e.printStackTrace();
}
}

显然这种方式会比最初的使用要简化很多,但是在查询处理的时候我们还是没有解决ResultSet结果集的处理问题,所以我们还需要继续优化

1.4 JDBC优化3.0

针对ResultSet的优化我们需要从反射和元数据两方面入手,具体如下。

/**
* 查询方法的简易封装
* @param sql
* @param clazz
* @param parameter
* @param <T>
* @return
* @throws Exception
*/
public static <T> List<T> query(String sql, Class clazz, Object ...
parameter) throws Exception{
conn = getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
if(parameter != null && parameter.length > 0){
for (int i = 0; i < parameter.length; i++) {
ps.setObject(i+1,parameter[i]);
}
}
ResultSet rs = ps.executeQuery();
// 获取对应的表结构的元数据
ResultSetMetaData metaData = ps.getMetaData();
List<T> list = new ArrayList<>();
while(rs.next()){
// 根据 字段名称获取对应的值 然后将数据要封装到对应的对象中
int columnCount = metaData.getColumnCount();
Object o = clazz.newInstance();
for (int i = 1; i < columnCount+1; i++) {
// 根据每列的名称获取对应的值
String columnName = metaData.getColumnName(i);
Object columnValue = rs.getObject(columnName);
setFieldValueForColumn(o,columnName,columnValue);
}
list.add((T) o);
}
return list;
}
/**
* 根据字段名称设置 对象的属性
* @param o
* @param columnName
*/
private static void setFieldValueForColumn(Object o, String
columnName,Object columnValue) {
Class<?> clazz = o.getClass();
try {
// 根据字段获取属性
Field field = clazz.getDeclaredField(columnName);
// 私有属性放开权限
field.setAccessible(true);
field.set(o,columnValue);
field.setAccessible(false);
}catch (Exception e){
// 说明不存在 那就将 _ 转换为 驼峰命名法
if(columnName.contains("_")){
Pattern linePattern = Pattern.compile("_(\\w)");
columnName = columnName.toLowerCase();
Matcher matcher = linePattern.matcher(columnName);
StringBuffer sb = new StringBuffer();
while (matcher.find()) {
matcher.appendReplacement(sb,
matcher.group(1).toUpperCase());
}
matcher.appendTail(sb);
// 再次调用复制操作
setFieldValueForColumn(o,sb.toString(),columnValue);
}
}
}

封装了以上方法后我们的查询操作就可以简化为

/**
*
* 通过JDBC查询用户信息
*/
public void queryUser(){
try {
String sql = "SELECT id,user_name,real_name,password,age,d_id from
t_user where id = ?";
List<User> list = DBUtils.query(sql, User.class,2);
System.out.println(list);
} catch (Exception e) {
e.printStackTrace();
}
}

这样一来我们在操作数据库中数据的时候就只需要关注于核心的SQL操作了。当然以上的设计还比较粗糙,这时Apache 下的 DbUtils是一个很好的选择

2.Apache DBUtils

官网地址https://commons.apache.org/proper/commons-dbutils/

2.1 初始配置

DButils中提供了一个QueryRunner类,它对数据库的增删改查的方法进行了封装,获取QueryRunner的方式

private static final String PROPERTY_PATH = "druid.properties";
private static DruidDataSource dataSource;
private static QueryRunner queryRunner;
public static void init() {
Properties properties = new Prop
erties();
InputStream in =
DBUtils.class.getClassLoader().getResourceAsStream(PROPERTY_PATH);
try {
properties.load(in);
} catch (IOException e) {
e.printStackTrace();
}
dataSource = new DruidDataSource();
dataSource.configFromPropety(properties);
// 使用数据源初始化 QueryRunner
queryRunner = new QueryRunner(dataSource);
}

创建QueryRunner对象的时候我们需要传递一个DataSource对象,这时我们可以选择Druid或者Hikai等常用的连接池工具,我这儿用的是Druid。

druid.username=root
druid.password=123456
druid.url=jdbc:mysql://localhost:3306/mybatisdb?characterEncoding=utf8&serverTimezone=UTC
druid.minIdle=10
druid.maxActive=30

2.2 基本操作

QueryRunner中提供的方法解决了重复代码的问题,传入数据源解决了资源管理的问题。而对于ResultSet结果集的处理则是通过 ResultSetHandler 来处理。我们可以自己来实现该接口

/**
* 查询所有的用户信息
* @throws Exception
*/
public void queryUser() throws Exception{
DruidUtils.init();
QueryRunner queryRunner = DruidUtils.getQueryRunner();
String sql = "select * from t_user";
List<User> list = queryRunner.query(sql, new
ResultSetHandler<List<User>>() {
@Override
public List<User> handle(ResultSet rs) throws SQLException {
List<User> list = new ArrayList<>();
while(rs.next()){
User user = new User();
user.setId(rs.getInt("id"));
user.setUserName(rs.getString("user_name"));
user.setRealName(rs.getString("real_name"));
user.setPassword(rs.getString("password"));
list.add(user);
}
return list;
}
});
for (User user : list) {
System.out.println(user);
}
}

或者用DBUtils中提供的默认的相关实现来解决
在这里插入图片描述

/**
* 通过ResultHandle的实现类处理查询
*/
public void queryUserUseBeanListHandle() throws Exception{
DruidUtils.init();
QueryRunner queryRunner = DruidUtils.getQueryRunner();
String sql = "select * from t_user";
// 不会自动帮助我们实现驼峰命名的转换
List<User> list = queryRunner.query(sql, new BeanListHandler<User>
(User.class));
for (User user : list) {
System.out.println(user);
}
}

通过Apache 封装的DBUtils是能够很方便的帮助我们实现相对比较简单的数据库操作

3.SpringJDBC

在Spring框架平台下,也提供的有JDBC的封装操作,在Spring中提供了一个模板方法
JdbcTemplate,里面封装了各种各样的 execute,query和update方法。
JdbcTemplate这个类是JDBC的核心包的中心类,简化了JDBC的操作,可以避免常见的异常,它封装了JDBC的核心流程,应用只要提供SQL语句,提取结果集就可以了,它是线程安全的。

3.1 初始配置

在SpringJdbcTemplate的使用中,我们依然要配置对应的数据源,然后将JdbcTemplate对象注入到IoC容器中。

@Configuration
@ComponentScan
public class SpringConfig {
@Bean
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUsername("root");
dataSource.setPassword("123456");
dataSource.setUrl("jdbc:mysql://localhost:3306/mybatisdb?
characterEncoding=utf-8&serverTimezone=UTC");
return dataSource;
}
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource){
JdbcTemplate template = new JdbcTemplate();
template.setDataSource(dataSource);
return template;
}
}

3.2 CRUD操作

在我们具体操作数据库中数据的时候,我们只需要从容器中获取JdbcTemplate实例即可

package com.gupaoedu.crud.jdbctemplate;
import com.gupaoedu.crud.domain.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
@Repository
public class UserDao {
@Autowired
private JdbcTemplate template;
public void addUser(){
int count = template.update("insert into
t_user(user_name,real_name)values(?,?)","bobo","波波老师");
System.out.println("count = " + count);
}
public void query1(){
String sql = "select * from t_user";
List<User> list = template.query(sql, new RowMapper<User>() {
@Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
User user = new User();
user.setId(rs.getInt("id"));
user.setUserName(rs.getString("user_name"));
user.setRealName(rs.getString("real_name"));
return user;
}
});
for (User user : list) {
System.out.println(user);
}
}
public void query2(){
String sql = "select * from t_user";
List<User> list = template.query(sql, new BeanPropertyRowMapper<>
(User.class));
for (User user : list) {
System.out.println(user);
}
}
}

4.Hibernate

前面介绍的Apache DBUtils和SpringJdbcTemplate虽然简化了数据库的操作,但是本身提供的功能还是比较简单的(缺少缓存,事务管理等),所以我们在实际开发中往往并没有直接使用上述技术,而是用到了Hibernate和MyBatis等这些专业的ORM持久层框架。

4.1 ORM介绍

ORM( Object Relational Mapping) ,也就是对象与关系的映射,对象是程序里面的对象,关系是它与数据库里面的数据的关系,也就是说,ORM框架帮助我们解决的问题是程序对象和关系型数据库的相互映射的问题

  • O:对象
  • M:映射
  • R:关系型数据库
  • 在这里插入图片描述

4.2 Hibernate的使用

Hibernate是一个很流行的ORM框架,2001年的时候就出了第一个版本。使用步骤如下

4.2.1 创建项目

创建一个Maven项目并添加相关的依赖即可,我们在此处直接通过 SpringDataJpa的依赖处理

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
4.2.2 配置文件

在使用Hibernate的使用,我们需要为实体类创建一些hbm的xml映射文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
'-//Hibernate/Hibernate Mapping DTD 3.0//EN'
'http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd'>
<hibernate-mapping>
<class name="com.gupaoedu.model.User" table="t_user">
<id name="id" />
<property name="userName" column="user_name"></property>
<property name="realName" column="real_name"></property>
</class>
</hibernate-mapping>

以及Hibernate的配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">
com.mysql.cj.jdbc.Driver
</property>
<property name="hibernate.connection.url">
jdbc:mysql://localhost:3306/mybatisdb?
characterEncoding=utf8&amp;serverTimezone=UTC
</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">123456</property>
<property name="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property>
<property name="hibernate.hbm2ddl.auto">update</property>
<mapping resource="User.hbm.xml"/>
</session-factory>
</hibernate-configuration>
4.2.3 CRUD 操作

然后在程序中我们可以通过Hibernate提供的 Session对象来实现CRUD操作

package com.gupaoedu;
import com.gupaoedu.model.User;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
public class HibernateTest {
/**
* Hibernate操作案例演示
* @param args
*/
public static void main(String[] args) {
Configuration configuration = new Configuration();
// 默认使用hibernate.cfg.xml
configuration.configure();
// 创建Session工厂
SessionFactory factory = configuration.buildSessionFactory();
// 创建Session
Session session = factory.openSession();
// 获取事务对象
Transaction transaction = session.getTransaction();
// 开启事务
transaction.begin();
// 把对象添加到数据库中
User user = new User();
user.setId(666);
user.setUserName("hibernate");
user.setRealName("持久层框架");
session.save(user);
transaction.commit();
session.close();
}
}
4.2.4 其他方式

在映射文件的位置,我们也可以通过注解的方式来替换掉映射文件

package com.gupaoedu.model;
import lombok.Data;
import org.springframework.cache.annotation.EnableCaching;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
@Data
@Entity
@Table(name = "t_user")
public class User {
@Id
@Column(name = "id")
private Integer id;
@Column(name = "user_name")
private String userName;
@Column(name = "real_name")
private String realName;
@Column(name = "password")
private String password;
@Column(name = "age")
private Integer age;
@Column(name = "i_id")
private Integer dId;
}

在Spring中给我们提供的JPA对持久层框架做了统一的封装,而且本质上就是基于HibernateJPA来实现的,所以我们在使用的时候也可以通过SpringDataJPA的API来操作
dao的接口只需要继承JpaRepository接口即可

public interface IUserDao extends JpaRepository<User,Integer> {
}

service层正常处理

package com.gupaoedu.service.impl;
import com.gupaoedu.dao.IUserDao;
import com.gupaoedu.model.User;
import com.gupaoedu.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceImpl implements IUserService {
@Autowired
private IUserDao dao;
@Override
public List<User> query() {
return dao.findAll();
}
@Override
public User save(User user) {
return dao.save(user);
}
}

4.3 Hibernate总结

Hibernate的出现大大简化了我们的数据库操作,同时也能够更好的应对更加复杂的业务场景,Hibernate具有如下的特点

  1. 根据数据库方言自定生成SQL,移植性好
  2. 自动管理连接资源
  3. 实现了对象和关系型数据的完全映射,操作对象就想操作数据库记录一样
  4. 提供了缓存机制Hibernate在处理复杂业务的时候同样也存在一些问题
  5. 比如API中的get(),update()和save()方法,操作的实际上是所有的字段,没有办法指定部分字段,换句话说就是不够灵活
  6. 自定生成SQL的方式,如果要基于SQL去做一些优化的话,也是非常困难的。
  7. 不支持动态SQL,比如分表中的表名,条件,参数变化等,无法根据条件自动生成SQL

因此我们需要一个更为灵活的框架

5.MyBatis简介

MyBatis官网地址

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

“半自动”的ORM框架能够很好的解决上面所讲的Hibernate的几个问题,半自动化”是相对于Hibernate的全自动化来说的。它的封装程度没有Hibernate那么高,不会自动生成全部的SQL语句,主要解决的是SQL和对象的映射问题。

MyBatis的前身是ibatis,2001年开始开发,是“internet”和“abatis ['æbətɪs](障碍物)”两个单词的组合。04年捐赠给Apache。2010年更名为MyBatis。

在MyBatis里面,SQL和代码是分离的,所以会写SQL基本上就会用MyBatis,没有额外的学习成本。

二、MyBatis实际案例

接下来我们就通过实际的案例代码来演示下MyBatis的具体使用。

1. 环境准备

我们先来搭建MyBatis的使用环境

1.1 创建项目

创建一个普通的Maven项目,然后添加对应的Mybatis和MySQL的相关依赖

<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>

1.2 POJO对象

我们的案例通过数据库中的 T_USER 表来讲解,创建的对应的POJO对象为,有用到Lombok,大家可以自行添加对应的依赖

@Data
public class User {
private Integer id;
private String userName;
private String realName;
private String password;
private Integer age;
private Integer dId;
}

1.3 添加配置文件

在MyBatis中我们需要添加全局的配置文件和对应的映射文件。全局配置文件,这里面是对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>
<properties resource="db.properties"></properties>
<settings>
<!-- 打印查询语句 -->
<setting name="logImpl" value="STDOUT_LOGGING" />
<!-- 控制全局缓存(二级缓存),默认 true-->
<setting name="cacheEnabled" value="false"/>
<!-- 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。默认 false -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 当开启时,任何方法的调用都会加载该对象的所有属性。默认 false,可通过select标签
的 fetchType来覆盖-->
<setting name="aggressiveLazyLoading" value="true"/>
<!-- Mybatis 创建具有延迟加载能力的对象所用到的代理工具,默认JAVASSIST -->
<!--<setting name="proxyFactory" value="CGLIB" />-->
<!-- STATEMENT级别的缓存,使一级缓存,只针对当前执行的这一statement有效 -->
<!--
<setting name="localCacheScope" value="STATEMENT"/>
-->
<setting name="localCacheScope" value="SESSION"/>
</settings>
<typeAliases>
<typeAlias alias="user" type="com.gupaoedu.domain.User" />
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/><!-- 单独使用时配置成MANAGED没有事务 -->
<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/UserMapper.xml"/>
</mappers>
</configuration>

关联的映射文件,通常来说一张表对应一个,我们会在这个里面配置我们增删改查的SQL语句,以及参数和返回的结果集的映射关系。

<?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.gupaoedu.mapper.UserMapper">
<resultMap id="BaseResultMap" type="user">
<id property="id" column="id" jdbcType="INTEGER"/>
<result property="userName" column="user_name" jdbcType="VARCHAR" />
<result property="realName" column="real_name" jdbcType="VARCHAR" />
<result property="password" column="password" jdbcType="VARCHAR"/>
<result property="age" column="age" jdbcType="INTEGER"/>
<result property="dId" column="d_id" jdbcType="INTEGER"/>
</resultMap>
<select id="selectUserById" resultMap="BaseResultMap" statementType="PREPARED"
>
select * from t_user where id = #{id}
</select>
<!-- $只能用在自定义类型和map上 -->
<select id="selectUserByBean" parameterType="user" resultMap="BaseResultMap"
>
select * from t_user where user_name = '${userName}'
</select>
<select id="selectUserList" resultMap="BaseResultMap" >
select * from t_user
</select>
</mapper>

数据库属性的配置文件一并贴出

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatisdb?characterEncoding=utf8&serverTimezone=UTC
jdbc.username=root
jdbc.password=123456

2. 编程式的使用

环境准备好后我们就可以来使用其帮助我们实现数据库的操作了。在MyBatis中的使用方式有两种
首先来看下第一种编程式的方式

/**
* MyBatis API 的使用
* @throws Exception
*/
@Test
public void test1() throws Exception{
// 1.获取配置文件
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
// 2.加载解析配置文件并获取SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
// 3.根据SqlSessionFactory对象获取SqlSession对象
SqlSession sqlSession = factory.openSession();
// 4.通过SqlSession中提供的 API方法来操作数据库
List<User> list =
sqlSession.selectList("com.gupaoedu.mapper.UserMapper.selectUserList");
for (User user : list) {
System.out.println(user);
}
// 5.关闭会话
sqlSession.close();
}

看到了执行效果
在这里插入图片描述

这种方式其实就是通过SqlSession中给我们提供的相关的API方法来执行对应的CRUD操作,查找我们写的SQL语句是通过 namespace+“.”+id的方式实现的
在这里插入图片描述

这样的调用方式,解决了重复代码、资源管理、SQL耦合、结果集映射这4大问题。
不过,这样的调用方式还是会存在一些问题:

  1. Statement ID是硬编码,维护起来很不方便;
  2. 不能在编译时进行类型检查,如果namespace或者Statement ID输错了,只能在运行的时候报错。

所以我们通常会使用第二种方式,也是新版的MyBatis里面推荐的方式:定义一个Mapper接口的方式。这个接口全路径必须跟Mapper.xml里面的namespace对应起来,方法也要跟Statement ID一一对应。

3. 代理方式的使用

我们还可以通过SqlSession中提供的getMapper方法来获取声明接口的代理对象来处理。实现如下

3.1 接口声明

我们需要声明一个Dao的接口。然后在接口中定义相关的方法。

/**
* Dao 的接口声明
*/
public interface UserMapper {
public List<User> selectUserList();
}

3.2 映射文件

我们通过getMapper的方式来使用的话,我们需要添加对应的映射文件,在映射文件中我们需要将namespace声明为上面接口的全类路径名,同时对应的sql标签的id要和方法名称一致
在这里插入图片描述
最后我们还有保证映射文件的名称和接口的名称要一致。在文件很多的情况能很好的管理

3.3 getMapper

最后我们在通过getMapper方法来获取声明的Dao接口的代码对象来实现数据库操作。

/**
 * MyBatis getMapper 方法的使用
*/
@Test
public void test2() throws Exception{
// 1.获取配置文件
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
// 2.加载解析配置文件并获取SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
// 3.根据SqlSessionFactory对象获取SqlSession对象
SqlSession sqlSession = factory.openSession();
// 4.通过SqlSession中提供的 API方法来操作数据库
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> list = mapper.selectUserList();
for (User user : list) {
System.out.println(user);
}
// 5.关闭会话
sqlSession.close();
}

通过执行接口方法,来执行映射器中的SQL语句。
最后总结下MyBatis的特点

  • 使用连接池对连接进行管理
  • SQL和代码分离,集中管理
  • 结果集映射
  • 参数映射和动态SQL
  • 重复SQL的提取
  • 缓存管理
  • 插件机制

Hibernate和MyBatis跟DbUtils、Spring JDBC一样,都是对JDBC的一个封装,我们去看源码,最后一定会看到Connection、Statement和ResultSet这些对象。

对应的选择

  • 在一些业务比较简单的项目中,我们可以使用Hibernate;

  • 如果需要更加灵活的SQL,可以使用MyBatis,对于底层的编码,或者性能要求非常高的场合,可以用JDBC;

  • 实际上在我们的项目中,MyBatis和Spring JDBC是可以混合使用的;

  • 当然,我们也根据项目的需求自己写ORM框架。

三、MyBatis核心配置

在MyBatis中我们发现其实他最核心的应该是那两个配置文件,一个全局配置文件,一个映射文件

我们只要把这两个文件弄清楚,其实对于MyBatis的使用就掌握了大部分。接下来我们详细的给大家来介绍下这两个配置文件

1.全局配置文件

MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下:

  • configuration(配置)
  • properties(属性)
  • settings(设置)
  • typeAliases(类型别名)
  • typeHandlers(类型处理器)
  • objectFactory(对象工厂)
  • plugins(插件)
  • environments(环境配置)
  • environment(环境变量)
  • transactionManager(事务管理器)
  • dataSource(数据源)
  • databaseIdProvider(数据库厂商标识)
  • mappers(映射器)

1.1 configuration

configuration是整个配置文件的根标签,实际上也对应着MyBatis里面最重要的配置类Configuration。它贯穿MyBatis执行流程的每一个环节。我们打开这个类看一下,这里面有很多的属性,跟其他的子标签也能对应上。

1.2 properties

第一个一级标签是properties,用来配置参数信息,比如最常见的数据库连接信息。

为了避免直接把参数写死在xml配置文件中,我们可以把这些参数单独放在properties文件中,用properties标签引入进来,然后在xml配置文件中用${}引用就可以了。可以用resource引用应用里面的相对路径,也可以用url指定本地服务器或者网络的绝对路径。

<?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"></properties>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/><!-- 单独使用时配置成MANAGED没有事务 -->
<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/UserMapper.xml"/>
</mappers>
</configuration>

1.3 settings

这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 下表描述了设置中各项设置的含义、默认值等。

设置参数描述有效值默认值
cacheEnabled全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存。truefalse
lazyLoadingEnabled延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态。truefalse
aggressiveLazyLoading当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载(参考lazyLoadTriggerMethods).truefalse
multipleResultSetsEnabled是否允许单一语句返回多结果集(需要兼容驱动)。truefalse
useColumnLabel使用列标签代替列名。不同的驱动在这方面会有不同的表现,具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果。truefalse
useGeneratedKeys允许JDBC支持自动生成主键,需要驱动兼容。如果设置为true则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍可正常工作(比如Derby)。truefalse
autoMappingBehavior指定MyBatis应如何自动映射列到字段或属性。NONE表示取消自动映射;PARTIAL只会自动映射没有定义嵌套结果集映射的结果集。FULL会自动映射任意复杂的结果集(无论是否嵌套)。NONE,PARTIAL,FULLPARTIAL
autoMappingUnknownColumnBehavior指定发现自动映射目标未知列(或者未知属性类型)的行为。NONE:不做任何反应WARNING:输出提醒日志('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior’的日志等级必须设置为WARN)FAILING:映射失败(抛出SqlSessionException)NONE,WARNING,FAILINGNONE
defaultExecutorType配置默认的执行器。SIMPLE就是普通的执行器;REUSE执行器会重用预处理语句(prepared statements);BATCH执行器将重用语句并执行批量更新。SIMPLE,REUSE,BATCHSIMPLE
defaultStatementTimeout设置超时时间,它决定驱动等待数据库响应的秒数。任意正整数NotSet(null)
defaultFetchSize为驱动的结果集获取数量(fetchSize)设置一个提示值。此参数只可以在查询设置中被覆盖。任意正整数NotSet(null)
safeRowBoundsEnabled允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为false。truefalse
safeResultHandlerEnabled允许在嵌套语句中使用分页(ResultHandler)。如果允许使用则设置为false。truefalse
mapUnderscoreToCamelCase是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名A_COLUMN到经典Java属性名aColumn的类似映射。true或falseFalse
localCacheScopeMyBatis利用本地缓存机制(Local Cache)防止循环引用(circularreferences)和加速重复嵌套查询。默认值为SESSION,这种情况下会缓存一个会话中执行的所有查询。若设置值为STATEMENT,本地会话仅用在语句执行上,对相同SqlSession的不同调用将不会共享数据。SESSION或STATEMENTSESSION
jdbcTypeForNull当没有为参数提供特定的JDBC类型时,为空值指定JDBC类型。某些驱动需要指定列的JDBC类型,多数情况直接用一般类型即可,比如NULL、VARCHAR或OTHER。JdbcType常量.大多都为:NULLVARCHARandOTHEROTHER
lazyLoadTriggerMethods指定哪个对象的方法触发一次延迟加载。用逗号分隔的方法列表。equals,clone,hashCode,toString
defaultScriptingLanguage指定动态SQL生成的默认语言。一个类型别名或完全限定类名。org.apache.ibatis.scripting.xmltags.XMLLanguageDriver
defaultEnumTypeHandler指定Enum使用的默认ypeHandler。(从3.4.5开始)一个类型别名或完全限定类名。org.apache.ibatis.type.EnumTypeHandler
callSettersOnNulls指定当结果集中值为null的时候是否调用映射对象的setter(map对象时为put方A法,这对于有Map.keySet()依赖或null值初始化的时候是有用的。注意基本类型(int、boolean等)是不能设置成null的。true或falsefalse
returnInstanceForEmptyRow当返回行的所有列都是空时,MyBatis默认返回null。当开启这个设置时,MyBatis会返回一个空实例。请注意,它也适用于嵌套的结果集(i.e.collectioin and association)。(从3.4.2开始)true或falsefalse
logPrefix指定MyBatis增加到日志名称的前缀。
logImpl指定MyBatis所用日志的具体实现,未指定时将自动查找。SLF4JLOG4J或LOG4J2或JDK_LOGGING或COMMONS_LOGGING
proxyFactory指定Mybatis创建具有延迟加载能力的对象所用到的代理工具。CGLIBJAVASSIST
vfsImpl指定VFS的实现自定义VFS的实现的类全限定名,以逗号分隔。Notset
useActualParamName允许使用方法签名中的名称作为语句参数名称。为了使用该特性,你的工程必须采用Java8编译,并且加上 parameters选项。(从3.4.1开始)true或falsetrue
configurationFactory指定一个提供Configuration实例的类。这个被返回的Configuration实例用来加载被反序列化对象的懒加载属性值。这个类必须包含一个签名方法staticConfiguration.getConfiguration().(从3.2.3版本开始)类型别名或者全类名.Notset

设置的案例

<settings>
<!-- 打印查询语句 -->
<setting name="logImpl" value="STDOUT_LOGGING" />
<!-- 控制全局缓存(二级缓存),默认 true-->
<setting name="cacheEnabled" value="false"/>
<!-- 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。默认 false -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 当开启时,任何方法的调用都会加载该对象的所有属性。默认 false,可通过select标签
的 fetchType来覆盖-->
<setting name="aggressiveLazyLoading" value="true"/>
<!-- Mybatis 创建具有延迟加载能力的对象所用到的代理工具,默认JAVASSIST -->
<!--<setting name="proxyFactory" value="CGLIB" />-->
<!-- STATEMENT级别的缓存,使一级缓存,只针对当前执行的这一statement有效 -->
<!--
<setting name="localCacheScope" value="STATEMENT"/>
-->
<setting name="localCacheScope" value="SESSION"/>
</settings>

1.4 typeAliases

TypeAlias是类型的别名,跟Linux系统里面的alias一样,主要用来简化类名全路径的拼写。比如我们的参数类型和返回值类型都可能会用到我们的Bean,如果每个地方都配置全路径的话,那么内容就比较多,还可能会写错。

我们可以为自己的Bean创建别名,既可以指定单个类,也可以指定一个package,自动转换。

<typeAliases>
<typeAlias alias="user" type="com.gupaoedu.domain.User" />
</typeAliases>

然后在使用的时候我们就可以简化了

在这里插入图片描述
MyBatis里面有很多系统预先定义好的类型别名,在TypeAliasRegistry中。所以可以用string代替java.lang.String。

public TypeAliasRegistry() {
registerAlias("string", String.class);
registerAlias("byte", Byte.class);
registerAlias("long", Long.class);
registerAlias("short", Short.class);
registerAlias("int", Integer.class);
registerAlias("integer", Integer.class);
registerAlias("double", Double.class);
registerAlias("float", Float.class);
registerAlias("boolean", Boolean.class);
registerAlias("byte[]", Byte[].class);
registerAlias("long[]", Long[].class);
registerAlias("short[]", Short[].class);
registerAlias("int[]", Integer[].class);
registerAlias("integer[]", Integer[].class);
registerAlias("double[]", Double[].class);
registerAlias("float[]", Float[].class);
registerAlias("boolean[]", Boolean[].class);
registerAlias("_byte", byte.class);
registerAlias("_long", long.class);
registerAlias("_short", short.class);
registerAlias("_int", int.class);
registerAlias("_integer", int.class);
registerAlias("_double", double.class);
registerAlias("_float", float.class);
registerAlias("_boolean", boolean.class);
registerAlias("_byte[]", byte[].class);
registerAlias("_long[]", long[].class);
registerAlias("_short[]", short[].class);
registerAlias("_int[]", int[].class);
registerAlias("_integer[]", int[].class);
registerAlias("_double[]", double[].class);
registerAlias("_float[]", float[].class);
registerAlias("_boolean[]", boolean[].class);
registerAlias("date", Date.class);
registerAlias("decimal", BigDecimal.class);
registerAlias("bigdecimal", BigDecimal.class);
registerAlias("biginteger", BigInteger.class);
registerAlias("object", Object.class);
registerAlias("date[]", Date[].class);
registerAlias("decimal[]", BigDecimal[].class);
registerAlias("bigdecimal[]", BigDecimal[].class);
registerAlias("biginteger[]", BigInteger[].class);
registerAlias("object[]", Object[].class);
registerAlias("map", Map.class);
registerAlias("hashmap", HashMap.class);
registerAlias("list", List.class);
registerAlias("arraylist", ArrayList.class);
registerAlias("collection", Collection.class);
registerAlias("iterator", Iterator.class);
registerAlias("ResultSet", ResultSet.class);
}

1.5 TypeHandler

由于Java类型和数据库的JDBC类型不是一一对应的(比如String与varchar、char、text),所以我们把Java对象转换为数据库的值,和把数据库的值转换成Java对象,需要经过一定的转换,这两个方向的转换就要用到TypeHandler。

当参数类型和返回值是一个对象的时候,我没有做任何的配置,为什么对象里面的一个String属性,可以转换成数据库里面的varchar字段?

这是因为MyBatis已经内置了很多TypeHandler(在type包下),它们全部全部注册在TypeHandlerRegistry中,他们都继承了抽象类BaseTypeHandler,泛型就是要处理的Java数据类型。

这个也是为什么大部分类型都不需要处理。当我们查询数据和登记数据,做数据类型转换的时候,就会自动调用对应的TypeHandler的方法。
在这里插入图片描述
我们可以自定义一个TypeHandler来帮助我们简单的处理数据,比如查询的结果的字段如果是一个字符串,且值为"zhangsan"就修饰下这个信息

package com.gupaoedu.type;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* 自定义的类型处理器
* 处理的字段如果是 String类型的话就 且 内容是 zhangsan 拼接个信息
* */
public class MyTypeHandler extends BaseTypeHandler<String> {
/**
* 插入数据的时候回调的方法
* @param ps
* @param i
* @param parameter
* @param jdbcType
* @throws SQLException
*/
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String
parameter, JdbcType jdbcType) throws SQLException {
System.out.println("---------------setNonNullParameter1:"+parameter);
ps.setString(i, parameter);
}
@Override
public String getNullableResult(ResultSet rs, String columnName) throws
SQLException {
String name = rs.getString(columnName);
if("zhangsan".equals(name)){
return name+"666";
}
return name;
}
@Override
public String getNullableResult(ResultSet rs, int columnIndex) throws
SQLException {
String name = rs.getString(columnIndex);
if("zhangsan".equals(name)){
return name+"666";
}
return name;
}
@Override
public String getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
String name = cs.getString(columnIndex);
if("zhangsan".equals(name)){
return name+"666";
}
return name;
}
}

同时将我们的处理器在全局配置文件中注册下

<typeHandlers>
<typeHandler handler="com.gupaoedu.type.MyTypeHandler"></typeHandler>
</typeHandlers>

然后我们在映射文件中配置对应的处理器
在这里插入图片描述
执行的效果
在这里插入图片描述

1.6 objectFactory

当我们把数据库返回的结果集转换为实体类的时候,需要创建对象的实例,由于我们不知道需要处理的类型是什么,有哪些属性,所以不能用new的方式去创建。只能通过反射来创建。

在MyBatis里面,它提供了一个工厂类的接口,叫做ObjectFactory,专门用来创建对象的实例(MyBatis封装之后,简化了对象的创建),里面定义了4个方法。

public interface ObjectFactory {
default void setProperties(Properties properties) {
// NOP
}
<T> T create(Class<T> type);
<T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object>
constructorArgs);
<T> boolean isCollection(Class<T> type);
}
方法作用
void setProperties(Properties properties);设置参数时调用
T create(Class type);创建对象(调用无参构造函数)
T create(Class type, List<Class<?>> constructorArgTypes, List constructorArgs);创建对象(调用带参数构造函数)
boolean isCollection(Class type)判断是否集合

ObjectFactory有一个默认的实现类DefaultObjectFactory。创建对象的方法最终都调用了instantiateClass(),这里面能看到反射的代码。
默认情况下,所有的对象都是由DefaultObjectFactory创建。

package com.gupaoedu.objectfactory;
import com.gupaoedu.domain.User;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
/**
*
* 自定义ObjectFactory,通过反射的方式实例化对象
* 一种是无参构造函数,一种是有参构造函数——第一个方法调用了第二个方法
*/
public class GpObjectFactory extends DefaultObjectFactory {
@Override
public Object create(Class type) {
System.out.println("创建对象方法:" + type);
if (type.equals(User.class)) {
User blog = (User) super.create(type);
blog.setUserName("object factory");
blog.setId(1111);
blog.setRealName("张三");
return blog;
}
Object result = super.create(type);
return result;
}
}

测试使用

public class ObjectFactoryTest {
public static void main(String[] args) {
GpObjectFactory factory = new GpObjectFactory();
User myBlog = (User) factory.create(User.class);
System.out.println(myBlog);
}
}

效果
在这里插入图片描述
如果在config文件里面注册,在创建对象的时候会被自动调用:

<!-- 对象工厂 -->
<objectFactory type="com.gupaoedu.objectfactory.GpObjectFactory">
<property name="gupao" value="666"/>
</objectFactory>

这样,就可以让MyBatis的创建实体类的时候使用我们自己的对象工厂。

1.7 plugins

插件是MyBatis的一个很强大的机制。跟很多其他的框架一样,MyBatis预留了插件的接口,让MyBatis更容易扩展。
Mybatis插件库

1.8 environments

environments标签用来管理数据库的环境,比如我们可以有开发环境、测试环境、生产环境的数据库。可以在不同的环境中使用不同的数据库地址或者类型。

<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/><!-- 单独使用时配置成MANAGED没有事务 -->
<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>
environment

一个environment标签就是一个数据源,代表一个数据库。这里面有两个关键的标签,一个是事务管理器,一个是数据源。

transactionManager

如果配置的是JDBC,则会使用Connection对象的commit()、rollback()、close()管理事务。

如果配置成MANAGED,会把事务交给容器来管理,比如JBOSS,Weblogic。因为我们跑的是本地程序,如果配置成MANAGE不会有任何事务。

如果是Spring + MyBatis,则没有必要配置,因为我们会直接在applicationContext.xml里面配置数据源和事务,覆盖MyBatis的配置。

dataSource

数据源,顾名思义,就是数据的来源,一个数据源就对应一个数据库。在Java里面,它是对数据库连接的一个抽象。

一般的数据源都会包括连接池管理的功能,所以很多时候也DataSource直接 称为连接池,准确的说法应该是:带连接池功能的数据源。

1.9 mappers

标签配置的是映射器,也就是Mapper.xml的路径。这里配置的目的是让MyBatis在启动
的时候去扫描这些映射器,创建映射关系。
我们有四种指定Mapper文件的方式:
四种配置
a.使用相对于类路径的资源引用(resource)

<mappers>
<mapper resource="UserMapper.xml"/>
</mappers>

b.使用完全限定资源定位符(绝对路径)(URL)

<mappers>
<mapper resource="file:///app/sale/mappers/UserMapper.xml"/>
</mappers>

c.使用映射器接口实现类的完全限定类名

<mappers>
<mapper class="com.gupaoedu.mapper.UserMapper"/>
</mappers>

d.将包内的映射器接口实现全部注册为映射器(最常用)

<mappers>
<mapper class="com.gupaoedu.mapper"/>
</mappers>

2. 映射文件

MyBatis 的真正强大在于它的语句映射,这是它的魔力所在。由于它的异常强大,映射器的 XML文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95%的代码。MyBatis 致力于减少使用成本,让用户能更专注于 SQL 代码。
SQL 映射文件只有很少的几个顶级元素(按照应被定义的顺序列出):

  • cache – 该命名空间的缓存配置。
  • cache-ref – 引用其它命名空间的缓存配置。
  • resultMap – 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。
  • parameterMap – 老式风格的参数映射。此元素已被废弃,并可能在将来被移除!请使用行内参数映射。文档中不会介绍此元素。
  • sql – 可被其它语句引用的可重用语句块。
  • insert – 映射插入语句。
  • update – 映射更新语句。
  • delete – 映射删除语句。
  • select – 映射查询语句。

2.1 cache

给定命名空间的缓存配置(是否开启二级缓存)。

2.2 cache-ref

其他命名空间缓存配置的引用。缓存相关两个标签我们在讲解缓存的时候会详细讲到。

2.3 resultMap

是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象。

<resultMap id="BaseResultMap" type="Employee">
<id column="emp_id" jdbcType="INTEGER" property="empId"/>
<result column="emp_name" jdbcType="VARCHAR" property="empName"/>
<result column="gender" jdbcType="CHAR" property="gender"/>
<result column="email" jdbcType="VARCHAR" property="email"/>
<result column="d_id" jdbcType="INTEGER" property="dId"/>
</resultMap>

2.4 sql

可被其他语句引用的可重用语句块。

<sql id="Base_Column_List">
emp_id, emp_name, gender, email, d_id
</sql>

2.5 增删改查标签

针对常用的增删改查操作提供的有对应的标签来处理

<insert> – 映射插入语句
<update> – 映射更新语句
<delete> – 映射删除语句
<select> – 映射查询语句
<select
id="selectPerson"
parameterType="int"
parameterMap="deprecated"
resultType="hashmap"
resultMap="personResultMap"
flushCache="false"
useCache="true"
timeout="10"
fetchSize="256"
statementType="PREPARED"
resultSetType="FORWARD_ONLY">
属性描述
id在命名空间中唯一的标识符,可以被用来引用这条语句。
parameterType将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。
parameterMap用于引用外部 parameterMap 的属性,目前已被废弃。请使用行内参数映射和 parameterType 属性。
resultType期望从这条语句中返回结果的类全限定名或别名。 注意,如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。 resultType 和
resultMap之间只能同时使用一个。
resultMap对外部 resultMap 的命名引用。结果映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂的映射问题都能迎刃而解。 resultType 和resultMap 之间只能同时使用一个。
flushCache将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:false。
useCache将其设置为 true 后,将会导致本条语句的结果被二级缓存缓存起来,默认值:对 select 元素为 true。
timeout这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。
fetchSize这是一个给驱动的建议值,尝试让驱动程序每次批量返回的结果行数等于这个设置值。 默认值为未设置(unset)(依赖驱动)。
statementType可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
resultSetTypeFORWARD_ONLY,SCROLL_SENSITIVE, SCROLL_INSENSITIVE 或DEFAULT(等价于 unset) 中的一个,默认值为 unset (依赖数据库驱动)。
databaseId如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有不带 databaseId 或匹配当前 databaseId 的语句;如果带和不带的语句都有,则不带的会被忽略。
resultOrdered这个设置仅针对嵌套结果 select 语句:如果为 true,将会假设包含了嵌套结果集或是分组,当返回一个主结果行时,就不会产生对前面结果集的引用。这就使得在获取嵌套结果集的时候不至于内存不够用。默认值: false 。
resultSets这个设置仅适用于多结果集的情况。它将列出语句执行后返回的结果集并赋予每个结果集一个名称,多个名称之间以逗号分隔。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值