MyBatis
一、MyBatis 概述
1. MyBatis介绍
Clinton Begin在2002年发起iBATIS项目,此后不久将项目捐献给了Apache Software Foundation,2010年这个项目由Apache Software Foundation迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
2. 依赖介绍
Mybatis的zip发行文件中除了核心jar文件外,还有其他jar,以下为各个jar的说明
1)、要使用 MyBatis, 只需将 mybatis-x.x.x.jar 文件置于 classpath 中即可
2)、以下这四个文件,除cglib和asm外都放入了 mybatis-x.x.x.jar文件中, mybatis加入了新的
classloader,classloader首先在classpath中找对应的类文件,如果找不到会在mybatis-x.x.x.jar
文件中查找
**动态代理工具:**
1. cglib:
asm-x.x:字节码工具
cglib-x.x代理生成工具
2. javassist
javassist-x.x:字节码工具,生成代理
如果延迟加载的话,需要为业务实体对象(bo、vo)生成代理,代理对象类的生成采用
cglib或者javassist,而cglib在处理字节码的地方采用了asm。可以通过配置项
proxyFactory=CGLIB|JAVASSIST修改代理类生成的方式,默认是javassist。Mapper接
口的代理是使用JDK动态代理生成的,不需要这些。
**对象图导航包:**
ognl-x.x:在动态构建SQL语句的时使用,${}
3)、mybatis对日志进行了对应的封装,根据配置项加载对应的日志实现类
<setting name="logImpl" value="LOG4J2" />
可选的值:SLF4J、LOG4J、LOG4J2、JDK_LOGGING、COMMONS_LOGGING、
STDOUT_LOGGING、NO_LOGGING
4)、ant:ant的两个包是cglib的编译依赖,cglib中使用了ant的类文件
Mybatis依赖说明:地址
二、工程搭建
1. 工程依赖
Mybatis的依赖项可以先下载好,然后导入工程。如果是Maven工程,使用Maven的依赖管理导入。以
下为MyBatis的pom文件
<?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.demo</groupId>
<artifactId>mybatis</artifactId>
<version>1.0-SNAPSHOT</version>
<name>mybatis</name>
<properties>
<!-- maven:编译版本,源码 字符集,或者可以配置maven-compiler-plugin -->
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>utf-8</project.build.sourceEncoding>
<!--依赖版本属性:版本的依赖关系可以查询maven中央仓库-->
<log4j2-version>2.7</log4j2-version>
<mysql-connector-java-version>8.0.17</mysql-connector-java-version>
<mybatis-version>3.4.6</mybatis-version>
</properties>
<dependencies>
<!--mysql-jdbc:mysql必选,JDBC驱动 -->
<!--依赖传递导入:protobuf-java-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql-connector-java-version}</version>
</dependency>
<!--mybatis:-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis-version}</version>
</dependency>
<!--log4j-core:可选,log4j2的核心包 -->
<!--依赖传递导入:log4j-api-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j2-version}</version>
</dependency>
</dependencies>
</project>
2. 配置文件
Mybatis是一个ORM工具,它借助JDBC完成与数据库的交互,程序中需要通过配置文件提供数据库相关
的配置信息,以及其它影响Mybatis执行过程的配置数据,在工程中创建配置文件,如:mybatisconfig.xml,以下是基本配置文件示例
1)、mybatis-config.xml
XML 配置文件中包含了对 MyBatis 系统的核心设置,包含获取数据库连接实例的数据源
(DataSource)和决定事务作用域和控制方式的事务管理器(TransactionManager)。
<?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="oracle.jdbc.driver.OracleDriver"/>
<property name="url"
value="jdbc:oracle:thin:@localhost:3306:XE"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/demo/mybatis/step01/conf/UserMapper.xml"/>
</mappers>
</configuration>
environment 元素体中包含了事务管理和连接池的配置。
mappers 元素则是包含一组映射器(mapper),这些映射器的 XML 映射文件包含了 SQL代码和映射定义信息。
3. 映射文件
Mybatis在执行ORM的过程中,可以将Java对象数据转化为SQL,也可以将SQL
UserMapper.xm
<?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.demo.mybatis.step01.bo.UserMapper">
<!--配置查询结果集和对象的映射,可以不进行配置,默认属性名和列名相同 -->
<resultMap id="user" type="com.demo.mybatis.step01.bo.User">
<id column="user_id" property="userID" jdbcType="INTEGER"/>
<result column="user_name" property="userName"/>
<result column="password" property="password"/>
</resultMap>
<select id="selectUserByID" resultMap="user">
select * from users where user_id = #{id}
</select>
</mapper>
在命名空间“com.demo.mybatis.step01.bo.UserMapper”中定义一个名为“selectUserByID”的映射语句 , 命名空间是必须的,目的是区分映射语句,而且最好是和对应的Mapper接口同名
4. Mybatis API
SqlSession
使用 MyBatis 的主要 Java 接口就是 SqlSession。可以通过这个接口来执行SQL,获取执行结果。
SqlSessions 是由 SqlSessionFactory 实例创建的,而 SqlSessionFactory 本身是由SqlSessionFactoryBuilder 创建的,它可以从 XML、注解或手动配置 Java 代码来创建SqlSessionFactory。
public class Step01 {
public static void main(String[] args) {
String resource = "com/demo/mybatis/step01/conf/mybatis-config.xml";
InputStream inputStream;
try {
// Resources 工具类,可以从 classpath 或其他位置加载资源文件。
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException();
}
// SqlSessionFactoryBuilder创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new
SqlSessionFactoryBuilder().build(inputStream);
//每个线程都应该有它自己的 SqlSession实例。SqlSession 的实例不是线程安全的
SqlSession session = sqlSessionFactory.openSession();
}
}
直接执行SQL
通过调用MyBatis中SqlSession对象的相关方法执行映射文件中配置的SQL,有一些类似DBUtils的操作
上述使用MyBatis的方法很简单,但是三层结构中实现起来比较繁琐,需要在DAO接口的实现类中分别
调用SQLSession对象的方法来执行映射文件中的SQL。
SqlSession session = sqlSessionFactory.openSession();
try {
User user = (User) session.selectOne("com.demo...selectUserByID", 4);
//session.delete("");
//session.update();
//session.insert()
//session.commit();
logger.info(user);
} finally {
session.close();
}
5. 代理实例
MyBatis可以使用JDK动态代理生成DAO接口的代理实例,在代理中根据接口方法名称自动执行映射文
件中对应的SQL。
通过SqlSession.getMapper(XXXMapper.class) 方法,MyBatis会根据相应的接口声明的方法信息,通过JDK动态代理机制生成一个代理实例,使用Mapper接口的某一个方法时,MyBatis会根据这个方法的方法名和参数类型,确定Statement Id,然后通过SqlSession.select(“statementId”,parameterObject)等来实现对数据库的操作
UserMapper.java
public interface UserMapper {
public User selectUserByID(int id);
}
Client.java
try {
UserMapper mapper = session.getMapper(UserMapper.class);
// 映射器是创建用来绑定映射语句的接口
User user = mapper.selectUserByID(4);
//mapper.delete(1);
session.commit();
logger.info(user);
} finally {
session.close();
}
三、配置文件
MyBatis使用 org.apache.ibatis.session.Configuration 对象作为所有配置信息的容器。配置信息可以
保存在XML文件中,也可以通过Java API配置
XML:所有配置信息放在XML文件中,MyBatis通过加载XML配置文件,将配置文信息组装成内部的Configuration对象
String resource = "com/demo/mybatis/step01/conf/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new
SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
try {
session.selectOne("com.....UserMapper.selectUserByID", 1);
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.selectUserByID(2);
} finally {
session.close();
}
Java API:这种方式不使用XML配置文件,需要手动创建Configuration对象,然后将配置参数set进入Configuration对象中
DataSource dataSource = DataSourceFactory.getDataSource();
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory,
dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(....);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().
build(configuration);
1. 属性(properties)
<properties resource="com/demo/mybatis/step02/conf/config.properties">
<property name="jdbc.user" value="hr" />
</properties>
<environments default="development">
<environment id="development">
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}" /> #使用属性文件的值
<property name="url" value="${jdbc.url}" /> #使用属性定义
</dataSource>
</environment>
</environments>
多个地方定义同名属性的优先级
在 properties 元素体内指定的属性首先被读取。
然后根据 properties 元素中的 resource 属性读取类路径下属性文件或根据 url 属性指定的
路径读取属性文件,并覆盖已读取的同名属性。
最后读取作为方法参数传递的属性,并覆盖已读取的同名属性
2. 设置(settings)
调整设置,改变 MyBatis的运行时行为
<settings>
<setting name="cacheEnabled" value="true" />
<setting name="logImpl" value="LOG4J2" />
<setting name="proxyFactory" value="CGLIB" />
<setting name="mapUnderscoreToCamelCase" value="true" />
</settings>
常用配置项
**cacheEnabled**:启用二级缓存,默认true
**logImpl**:指定 MyBatis 所用日志的具体实现,未指定时将自动查找。日志会打印非常详细的内容,某些查询可能会返回大量的数据,只想记录其执行的SQL语句的话,可以将日志级别设为DEBUG。
可使用如下值:SLF4J、LOG4J、LOG4J2、JDK_LOGGING、COMMONS_LOGGING、STDOUT_LOGGING、 NO_LOGGING未指定时将自动查找。
debug日志级别:只打印sql和参数,trace日志级别:打印执行结果集
**proxyFactory**:延迟加载所用到的代理工具。CGLIB | JAVASSIST
**mapUnderscoreToCamelCase**:是否开启自动驼峰命名规则映射,即从经典数据库列名A_COLUMN 到经典Java 属性名 aColumn 的类似映射。默认False
3. 类型别名(typeAliases)
类型别名是为 Java 类型设置一个短的名字,存在的意义仅在于用来减少类完全限定名的冗余,Mybtis已
经为许多常见的 Java 类型内建了相应的类型别名。它们都是大小写不敏感的,如:List的内置别名为list
<typeAliases>
<typeAlias alias="User" type="com.demo.mybatis.step02.bo.User" />
<package name="com.demo.mybatis"/>
</typeAliases>
typeAlias可以给单独的一个类起别名,通过package可以给指定包下的类起别名,类名首字母小写
4. 类型处理器(typeHandlers)
类型处理器是在预处理语句(PreparedStatement)中设置一个参数时或者从结果集中取出一个值时,用来将获取的值以合适的方式转换成 Java 类型。可以重写类型处理器或创建自己的类型处理器来处理不支持的或非标准的类型
<typeHandlers>
<typeHandler handler="org.mybatis.example.ExampleTypeHandler"/>
</typeHandlers>
常见类型映射
MySQL | Oralce | Java | JDBC |
---|---|---|---|
int | number§ | java.lang.Integer, | int NUMERIC 或 INTEGER |
bigint | number§ | java.lang.Long , | |
float | number(p,s) | java.lang.Float, | float NUMERIC 或 FLOAT |
decimal | number(p,s) | java.math.BigDecimal | NUMERIC 或 DECIMAL |
char | char java.lang.String | CHAR, | VARCHAR |
varchar | varchar2 | java.lang.String | CHAR, VARCHAR |
date | java.util. | Date | |
timestamp | java.util.Date | TIMESTAMP | |
datetime | date | java.util.Date | DATE |
**timestamp**:mysql可以设置默认值及自动更新,但是oracle的只是比date的精度要高,没有默认值及自动更新。
MySQL=>8.0.2的版本中默认值及自动更新默认是关闭的,可以通过设置系统变量开启。
也可以添加子句到某一datetime列: DEFAULT CURRENT_TIMESTAMP
ON UPDATE CURRENT_TIMESTAMP
**number(p,s)**:p表示总位数,s表示其中小数位数
**decimal**:精确数值数据,最大位数可以是65
例如:decimal(5,2)能够存储具有五位数和两位小数的任何值,因此可以存储范围为-999.99至999.99。
5. 环境配置(environments)
MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多个数据库环境之中,例如,开发、测试和生产环境需要有不同的配置。尽管可以配置多个环境,每个SqlSessionFactory 实例只能选择其一。
<environments default="development">
<environment id="development1">
<transactionManager type="JDBC">
...
</transactionManager>
<dataSource type="POOLED">
...
</dataSource>
</environment>
<environment id="development2">
<transactionManager type="JDBC">
...
</transactionManager>
<dataSource type="POOLED">
...
</dataSource>
</environment>
</environments>
数据源(dataSource)
MyBatis在初始化时,根据配置文件的属性来创建相应类型的的数据源DataSource,MyBatis内置了三
种数据源,当然也可以使用自定义或者第三方数据源。
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED" >
<property name="driver" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:@localhost:1521:XE"/>
<property name="username" value="hr"/>
<property name="password" value="hr"/>
</dataSource>
</environment>
</environments>
type=”POOLED”
:创建PooledDataSource实例,池化数据源 type=”UNPOOLED”
:创建UnpooledDataSource实例,非池化数据源 type=”JNDI”
:从JNDI服务上查找DataSource实例type=“其他数据源实现类名
通过SqlSession执行SQL时,Executor使用事务管理器的getConnection()获取连接,事务的管理方法由
SqlSession调用executor,executor调用事务管理器完成。
事务管理(transactionManager)
事务管理器的配置 ,在 MyBatis 中有两种类型的事务管理器(也就是 type=”[JDBC|MANAGED]”)
- 使用JDBC的事务管理机制:即利用java.sql.Connection对象完成对事务的提交(commit())、回
滚(rollback())、关闭(close())等 - 使用MANAGED的事务管理机制:这种机制MyBatis自身不会去实现事务管理,而是让其他容器如
(Spring、Tomcat)来实现对事务的管理
<transactionManager type="JDBC"/>
JDBC(JdbcTransaction ):使用java.sql.Connection对象完成事务的提交(commit())、回滚
(rollback())、关闭(close())等
MANAGED(ManagedTransaction ):MyBatis自身不会去实现事务管理,而是让程序的容器如
(Tomcat、Spring)来实现对事务的管理,commit())、回滚(rollback())的实现方法为空
Transaction.java
public interface Transaction {
Connection getConnection() throws SQLException;
void commit() throws SQLException;
void rollback() throws SQLException;
void close() throws SQLException;
Integer getTimeout() throws SQLException;
}
6.数据库厂商标识(databaseIdProvider)
MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的databaseId 属性.
<databaseIdProvider type="DB_VENDOR">
<property name="SQL Server" value="sqlserver" />
<property name="DB2" value="db2" />
<property name="Oracle" value="oracle" />
</databaseIdProvider>
这里的 DB_VENDOR 会通过 DatabaseMetaData#getDatabaseProductName() 返回的字符串进行设置。 由于通常情况下这个字符串都非常长而且相同产品的不同版本会返回不同的值,所以最好通过设置属性别名来使其变短
<insert id="insertAuthor" databaseId="oracle" >
如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有的不带 databaseId或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略
7.映射器(mappers)
SQL 映射文件的位置
<mappers>
<!-- classpath 相对路径 -->
<mapper resource="com/demo/mybatis/step02/conf/UserMapper.xml" />
<!-- 绝对路径 -->
<!--
<mapper url="file:d:/com/demo/mybatis/step02/conf/UserMapper.xml" />
-->
<!-- 通过Mapper接口来指定 -->
<!--
<mapper class="com.demo.mybatis.step02.bo.UserMapper" />
-->
<!-- 包下的所有接口都注册为mapper -->
<!--
<package name="com.demo.mybatis.step02.bo" />
-->
</mappers>
四、映射文件
映射文件用来定义SQL映射信息的,SQL 映射文件只有很少的几个顶级元素(按照定义的顺序列出)
cache – 对给定命名空间的缓存配置。
cache-ref – 对其他命名空间缓存配置的引用。
resultMap – 描述如何从数据库结果集中来映射对象。
sql – 可被其他语句引用的可重用语句块。
insert – 映射插入语句
update – 映射更新语句
delete – 映射删除语句
select – 映射查询语句
<?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.demo.mybatis.step01.bo.UserMapper">
<resultMap id="user" type="com.demo.mybatis.step01.bo.User">
<id column="user_id" property="userID" jdbcType="VARCHAR"/>
<result column="user_name" property="userName"/>
<result column="password" property="password"/>
</resultMap>
<select id="selectUserByID" resultMap="user">
select * from users where
user_id = #{id}
</select>
</mapper>
在命名空间“com.demo.mybatis.step01.bo.UserMapper”中定义一个名为“selectUserByID”的映射语句,
命名空间是必须的,目的是区分映射语句,而且最好是和对应的Mapper接口同名
1. 参数传递
Mybatis允许基本类型、Map、实体对象作为参数,通过以下两种方式替换到SQl中
#{paramName} :创建 PreparedStatement 参数占位符并安全地设置参数(就像使用 ? 一样)
${parmName}:直接在 SQL 语句中插入一个不转义的字符串,可能会有SQL注入问题。
JDBC 要求,如果一个列允许 null 值,并且会传递值 null 的参数,就必须要指定 JDBC Typ
#{userID,jdbcType=NUMERIC}
传多个参数的方案:
#{0}, #{1}
用Map
使用注解:
electUser(@param(“userName”)String name);
user_name = #{userName,jdbcType=VARCHAR}
Mapper接口
public interface UserMapper {
/**
* 插入数据,返回主键,主键会设置到user中
*
* @param user
* @return
*/
public int insertUser(User user);
/**
* 更新数据
*
* @param user
*/
public void updateUser(User user);
/**
* 删除数据
*
* @param userID
*/
public void deleteUser(int userID);
/**
* 单表查询,返回一个对象
*
* @param id
* @return
*/
public User selectUserByID(int id);
/**
* 分页查询数据,参数中需要传入分页的数据
*
* @return
*/
public List<User> selectUsersOfPager(Map<String, Object> params);
/**
* 根据条件查询符合条件的记录总数,用户分页
*
* @return
*/
public int selectUsersOfPagerCountAll(Map<String, Object> params);
}
映射文件
使用map中的参数,直接指定map的key
#{startIndex},#{pageSize}
使用实体对象中的属性,指定属性名称
#{userName}
使用基本类型参数,引用时参数名称可以与方法参数不同名
#{userID}
2. insert
定义插入语句,返回的是插入多少行,返回主键需要通过selectKey或者getGeneratedKeys 获取后,设置到实体的属性中。
<insert
id="insertAuthor"
parameterType="domain.blog.Author"
flushCache="true"
keyProperty="unset"
useGeneratedKeys=""
databaseId="oralce">
**parameterType**:参数类型,可以不配置,MyBatis可以根据方法参数推导,默认值为未设置(unset)
**flushCache**:其置为 true,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:true(对于 insert、update 和 delete 语句)
**keyProperty**: 唯一标记一个属性,MyBatis 会通过 getGeneratedKeys 的返回值或者通过
insert 语句的 selectKey 子元素设置它的键值,默认值:未设置( unset )。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。
**useGeneratedKeys**: 这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键
(比如:像 MySQL 和 SQL Server 这样的关系数据库管理系统的自动递增字段),默认值:false。
**databaseId**:如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;
如果带或者不带的语句都有,则不带的会被忽略
insert_oracle
<!-- insert<单行、返回主键> -->
<!-- insert<单行、返回主键> -->
<insert id="insertUser">
<selectKey resultType="int" order="BEFORE" keyProperty="userID">
SELECT seq_id.nextval as user_id from DUAL
</selectKey>
insert into users (user_id,user_name,password)
values
(#{userID},#{userName},#{password})
</insert>
<!-- insert<多行> -->
<insert id="insertUsers">
insert into users (user_id,user_name,password)
select
seq_id.nextval,a.*
from(
<foreach collection="list" item="user" separator="UNION ALL">
select #{user.userName},#{user.password}
from dual
</foreach>
) a
</insert>
insert_mysql
<!-- insert<单行、返回主键> -->
<insert id="insertUser" useGeneratedKeys="true" keyProperty="userID">
insert into users (user_id,user_name,password)
values
(#{userID},#{userName},#{password})
</insert>
<!-- 或者 -->
<insert id="insertUser" >
<selectKey keyProperty="userID" order="AFTER"
resultType="java.lang.Integer">
SELECT LAST_INSERT_ID()
</selectKey>
insert into users (user_name,password)
values
(#{userName},#{password})
</insert>
3. delelte
定义删除语句,返回的是删除多少行。
<delete
id="deleteAuthor"
parameterType="domain.blog.Author"
flushCache="true">
示例
<!-- delete<单行> -->
<delete id="deleteUser">
delete from users where user_id = #{userID}
</delete>
<!-- delete<多行> -->
<delete id="deleteUsers">
delete from users
<where>
<foreach collection="list" index="index" item="user" open="(" separator="or" close=")">
user_id=#{user.userID}
</foreach>
</where>
</delete>
4. update
定义更新语句,返回的是更新多少行。
<update
id="updateAuthor"
parameterType="domain.blog.Author"
flushCache="true">
update_mysql
<!-- update<单行> -->
<update id="updateUser">
update users set
user_name = #{userName},
password = #{password}
where user_id = #{userID}
</update>
5. select
1. 查询定义
定义查询语句,返回的是映射结果。
< select
id="selectPerson"
parameterType="int"
resultType="hashmap"
resultMap="personResultMap"
flushCache="false"
useCache="true"
databaseId="oracle">
id:在命名空间中唯一的标识符,可以被用来引用这条语句。
parameterType:将会传入这条语句的参数类的完全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过调用方法的参数推断出参数的类型,从而在设置sql的参数时,根据参数类型选择对应的typeHandler,默认值为 unset。
resultType:从这条语句中返回的期望类型的类的完全限定名或别名。注意如果是集合情形,那应该是集合可以包含的类型,而不能是集合本身。使用 resultType 或 resultMap,但不能同时使用。
resultMap:外部 resultMap 的命名引用,引用其他映射文件的resultMap需要加名称空间。使用 resultMap 或 resultType,但不能同时使用。
flushCache:将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:false。
useCache:将其设置为 true,将会导致本条语句的结果被二级缓存,默认值:对 select 元素为 true。
databaseId:如果配置了 databaseIdProvider,MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。如果配置了databaseId 属性,则id属性的值可以相同
示例
<!-- ************ select<单表> ************ -->
<select id="selectUserByID" resultMap="userMap">
select * from users where
user_id = #{id}
</select>
<select id="selectAllUsers" resultMap="userMap">
select * from users
</select>
2. resultMap
resultMap是最复杂也是最强大的元素,用来描述如何从数据库结果集中加载对象
<resultMap id="userMap" type="com.demo.....bo.User" autoMapping="NONE"
extends="" >
<id/> #主键
<result/> #普通列
<association>< /association> #关联一对一
<collection></ collection> #关联一对多
<discriminator>< /discriminator> #使用返回列的值,动态映射关联数据
</resultMap>
id:resultMap的标识,被select元素引用
type:实体类名或类型别名,内建类型别名详情查阅:
autoMapping:有三种自动映射等级
NONE - 禁用自动映射。仅对手动映射的属性进行映射。
PARTIAL - 对除在内部定义了嵌套结果映射以外的属性进行映射,默认值
FULL - 自动映射所有属性。
注意:默认忽略大小写后全名匹配,如果需要下划线(A_COLUMN )转驼峰
aColumn ),则配置全局属性mapUnderscoreToCamelCase=true
extends:继承其他resultMap
单表映射
<resultMap id="userMap" type="com.demo.mybatis.step04.bo.User">
<id column="user_id" property="userID" jdbcType="INTEGER" />
<result column="user_name" property="userName" jdbcType="VARCHAR" />
<result column="password" property="password" jdbcType="VARCHAR" />
</resultMap>
id:映射主键,标记出作为 ID 的结果可以帮助提高整体性能
result:普通列的映射column、property要有,jdbcType、javaType不需要,自动推导
一对一映射
两种加载关联数据的方式:
嵌套查询:通过执行另外一个 SQL映射语句来返回预关联的数据。
嵌套结果:通过一个连接语句来查询关联数据。
嵌套结果
association关联本身可以是一个 resultMap 元素,或者从别处引用一个,引用其他映射文件的
resultMap需要加名称空间
<resultMap type="com.demo.mybatis.step04.bo.User" id="userAndInfoMap"
extends="userMap">
<association property="userInfo"
javaType="com.demo.mybatis.step04.bo.UserInfo" >
<id property="userID" column="user_id" />
<result property="name" column="name" />
<result property="sex" column="sex" />
<result property="birth" column="birth" />
<result property="love" column="love" />
<result property="place" column="place" />
<result property="phone" column="phone" />
<result property="newDate" column="newDate" />
<result property="updateDate" column="updateDate" />
</association>
</resultMap>
association:
column:主表中的哪一列对应对这个关联映射。可选。
property:映射列的字段或属性,可以使用通过点式的复杂属性导航,如果:“address.street” 。
javaType:一个Java类的完全限定名,或一个类型别名。如果映射到 JavaBean,MyBatis可
以推断类型。如果映射到的是 HashMap,则需要明确指定,如:hashmap
resultMap:引用的resultMap的id
columnPrefix:列前缀,统一区分多表的重名列
notNullColumn:默认情况下,子对象仅在至少一个列映射到其属性非空时才创建。 通过这个属性可以指定哪 个列不空的时候需要创建关联对象,可以指定多个列名,使用逗号分隔。
默认值:未设置(unset)。
嵌套查询
关联数据通过另一个SQL查询
<resultMap type="com.demo.mybatis.step04.bo.User" id="userAndInfoMap"
extends="userMap">
<association property="userInfo" column="user_id" select="selectUserInfo"
fetchType="lazy"/>
</resultMap>
association:
column:传递给嵌套查询语句的关联列, 可以指定多个列名通过column=”{prop1=col1,prop2=col2}”
property:映射列的字段或属性,可以使用通过点式的复杂属性导航,如果:“address.street” 。
select:另外一个映射语句的 ID,列属性中指定的列的值将被传递给目标 select 语句作为参数
fetchType:可选的。有效值为 lazy和eager。默认使用设置项lazyLoadingEnabled=“fale”
lazy:使用CGLLIB或者javassist生成当前映射对象的代理,在获取读取属性时在去加载数据,对应SQLSession不能关闭
一对多映射
通过collection映射集合
<resultMap type="com.demo.mybatis.step04.bo.User" id="userAndRoleMap">
<id property="userID" column="user_id" />
<result property="userName" column="user_name" />
<result property="password" column="password" />
<!-- 集合的嵌套结果 -->
<!-- ofType指定List中放的元素 -->
<collection property="roles" ofType="com.demo.mybatis.step04.bo.Role">
<id property="roleID" column="role_id" />
<result property="roleName" column="role_name" />
</collection>
<!-- 集合的嵌套查询 -->
<!--
<collection property="roles" javaType="ArrayList" column="user_id"
ofType="com.demo.mybatis.step04.bo.Role" select="selectRolesForUser"/>
-->
</resultMap>
collection:
ofType:指定的是集合中的元素的类型,如果需要指定属性的类型可以使用javaType
多对多映射
通过 collection、association的嵌套可以完成一对一、一对多、多对多的反向映射
<resultMap type="com.demo.mybatis.step04.bo.User" id="usersAndRolesMap">
<id property="userID" column="user_id" />
<result property="userName" column="user_name" />
<result property="password" column="password" />
<collection property="roles" ofType="com.demo.mybatis.step04.bo.Role">
<id property="roleID" column="role_id" />
<result property="roleName" column="role_name" />
<!-- 返回的users中只有包含当前role对象的user -->
<collection property="users" ofType="com.demo.mybatis.step04.bo.User">
<id property="userID" column="user_id"/>
</collection>
</collection>
</resultMap>
动态映射
有时候,一个数据库查询可能会返回多个不同的结果集(但总体上还是有一定的联系的)。 鉴别器(discriminator)元素就是被设计来应对这种情况的,另外也能处理其它情况,例如类的继承层次结构。 鉴别器的概念很好理解——它很像 Java 语言中的 switch 语句。一个鉴别器的定义需要指定 column 和 javaType 属性。column 指定了 MyBatis 查询被比较值的地方。 而 javaType 用来确保使用正确的相等测试(虽然很多情况下字符串的相等测试都可以工作)。例如:
<resultMap id="vehicleResult" type="Vehicle">
<id property="id" column="id" />
<result property="vin" column="vin"/>
<result property="year" column="year"/>
<result property="make" column="make"/>
<result property="model" column="model"/>
<result property="color" column="color"/>
<discriminator javaType="int" column="vehicle_type">
<case value="1" resultMap="carResult"/>
<case value="2" resultMap="truckResult"/>
<case value="3" resultMap="vanResult"/>
<case value="4" resultMap="suvResult"/>
</discriminator>
</resultMap>
3. 查询缓存
基本原理
一级缓存
MyBatis会在表示会话的SqlSession对象中建立一个简单的缓存,将每次查询到的结果结果缓存起来,当下次查询的时候,如果判断先前有个完全一样的查询,会直接从缓存中直接将结果取出,返回给用户,不需要再进行一次数据库查询。MyBatis的一级缓存默认开启,通过配置无法禁用,可以实现插件禁用,或者在SQL中插入非业务条件表达式,如:#{timeString}=#{timeString}。
通过SqlSession执行SQL时,SqlSession会调用Executor完成具体的任务,Executor通过Cache缓存对象。Executor的实现都是由抽象类BaseExecutor扩展而来,BaseExecutor持有的Cache实现是PerpetualCache,PerpetualCache内部通过一个简单HashMap<k,v> 来实现的,没有其他的任何限制。
MyBatis创建一个新的SqlSession对象时,SqlSession对象中会有一个新的Executor对象,Executor对象中持有一个新的PerpetualCache对象;当会话结束时,SqlSession对象及其内部的Executor对象还有PerpetualCache对象也一并释放掉。
一级缓存是一个粗粒度的缓存,没有更新缓存和缓存过期的概念。SqlSession的close()方法会释放掉一级缓存PerpetualCache对象,SqlSession的clearCache()方法会清空PerpetualCache对象中的数据,只要调用该SqlSession的任何一个update操作(update()、delete()、insert()) ,都会清空PerpetualCache对象的数据。
二级缓存
二级缓存是应用级别的缓存,需要在全局配置文件中开启(cacheEnabled=true),同时在Mapper(或)和select语句( useCache=true)上开启。
SqlSession对象会使用Executor对象来完成会话操作,如果配置了"cacheEnabled=true",那么MyBatis在为SqlSession对象创建Executor对象时,会对Executor对象加上一个装饰者:CachingExecutor,这时SqlSession使用CachingExecutor对象来完成操作请求。CachingExecutor对于查询请求,会先判断该查询请求在二级缓存中是否有缓存结果,如果有查询结果,则直接返回缓存结果;如果缓存中没有,再交给真正的Executor对象来完成查询操作,之后CachingExecutor会将真正Executor返回的查询结果放置到缓存中,然后再返回给用户。
MyBatis的二级缓存设计得比较灵活,可以使用MyBatis自己定义的二级缓存实现;也可以通过实现Cache接口自定义缓存;也可以使用第三方内存缓存库,如Memcached等。
MyBatis通过Mapper组织缓存数据,可以为每一个Mapper分配一个Cache缓存对象(节点配置),也可以多个Mapper共用一个Cache缓存对象(使用节点配置),当两个Mapper存在关联查询时,要注意
缓存不同步。如:A Mappe和B Mappe使用不同缓存对象,A Mapper里通过关联查询缓存了BMapper的数据,如果这时对B Mapper进行增删改操作,B Mapper的缓存数据清除了,但是A Mappe里有关B表的缓存数据还在,如果要实现精确同步,需要实现插件。
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
映射语句文件中的所有 select 语句的结果将会被缓存。
映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
可用的清除策略有:
LRU – 最近最少使用:移除最长时间不被使用的对象。
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
flushCache:可以加在select(默认false)、udpate(默认true)、deleteudpate(默认true)、
insertudpate(默认true)中
useCache:将其设置为 true 后,将会导致本条语句的结果被二级缓存缓存起来,默认值:对 select 元素为 true。
3. sql
可被其他语句引用的可重用语句块。可以跨Mapper引用,跨Mapper引用需要加名称空间
<!--定义SQL-->
<sql id="userColumns"> ${alias}.user_id, ${alias}.user_name,
${alias}.password</sql>
<!--引用SQL-->
<select id="selectUsers" resultType="map">
select
<include refid="userColumns"><property name="alias" value="t1"/></include>,
<include refid="userColumns"><property name="alias" value="t2"/></include>
from some_table t1
cross join some_table t2
</select>
5. 动态SQL
if
动态 SQL 通常要做的事情是根据条件包含 where 子句的一部分
<select id="selectUser0" resultMap="userMap">
select
<include refid="userColumns">
<property name="alias" value="t1" />
</include>
from users t1 left join user_info t2 on t1.user_id=t2.user_id
where
<if test="userName != null">
t1.user_name like #{userName}
</if>
<if test="password != null">
and t1.password like #{password}
</if>
</select>
如果没有传入“userName”,则不生成t1.user_name like #{userName}
如果没有传入“password”,则不生成t1.passwordlike #{password}
<!-- 多种数据库支持,需要配置databaseIdProvider元素 -->
<select id="selectUser6" resultMap="userMap">
<if test="_databaseId == 'oracle'">
select seq_id.nextval from dual
</if>
<if test="_databaseId == 'db2'">
select nextval for seq_users from sysibm.sysdummy1"
</if>
</select>
choose (when, otherwise)
有时我们不想应用到所有的条件语句,而只想从中择其一项。针对这种情况,MyBatis 提供了 choose
元素,它有点像 Java 中的 switch 语句。
<select id="selectUser1" resultMap="userMap">
select
<include refid="userColumns">
<property name="alias" value="t1" />
</include>
from users t1 left join user_info t2 on t1.user_id=t2.user_id
where
<choose>
<when test="userName != null">
t1.user_name like #{userName}
</when>
<when test="password != null">
and password like #{password}
</when>
<otherwise>
and 1 = 1
</otherwise>
</choose>
</select>
trim (where, set)
where 元素只会在至少有一个子元素的条件返回 SQL 子句的情况下才去插入“WHERE”子句。而且,若
语句的开头为“AND”或“OR”,where 元素也会将它们去除。
<select id="selectUser2" resultMap="userMap">
select
<include refid="userColumns">
<property name="alias" value="t1" />
</include>
from users t1 left join user_info t2 on t1.user_id=t2.user_id
<where>
<if test="userName != null">
t1.user_name like #{userName}
</if>
<if test="password != null">
and t1.password like #{password}
</if>
</where>
</select>
类似的用于动态更新语句的解决方案叫做 set
<!-- set 元素会动态前置 SET 关键字,同时也会消除无关的逗号 -->
<update id="updateUser">
update users
<set>
<if test="username != null">user_name=#{username},</if>
<if test="password != null">password=#{password}</if>
</set>
where user_id=#{userID}
</update>
通用添加前缀,去除子句前缀的trim
<!-- trim 元素和where元素类似 -->
<select id="selectUser3" resultMap="userMap">
select
<include refid="userColumns">
<property name="alias" value="t1" />
</include>
from users t1 left join user_info t2 on t1.user_id=t2.user_id
<trim prefix="where" prefixOverrides="AND |OR ">
<if test="userName != null">
t1.user_name like #{userName}
</if>
<if test="password != null">
and t1.password like #{password}
</if>
</trim>
</select>
foreach
可以将任何可迭代对象(如列表、集合等)和任何的字典或者数组对象传递给foreach作为集合参数。
当使用可迭代对象或者数组时,index是当前迭代的次数,item的值是本次迭代获取的元素。当使用字
典(或者Map.Entry对象的集合)时,index是键,item是值
<select id="selectUser4" resultMap="userMap">
select
<include refid="userColumns">
<property name="alias" value="t1" />
</include>
from users t1 left join user_info t2 on t1.user_id=t2.user_id
where
user_id in
<foreach item="user" index="index" collection="list" open="("
separator="," close=")">
#{user.userID}
</foreach>
</select>
bind 元素可以从 OGNL 表达式中创建一个变量并将其绑定到上下文
<select id="selectUser5" resultMap="userMap">
<bind name="pattern" value="'%' + userName + '%'" />
select
<include refid="userColumns">
<property name="alias" value="t1" />
</include>
from users t1 left join user_info t2 on t1.user_id=t2.user_id
where
user_name like #{pattern}
</select>
五、注解映射
MyBatis 3提供注解来实现简单映射语句,但是Java 注解的的表达力和灵活性十分有限,如:动态SQL
是不能再注解中使用的,所以不建议使用注解定义映射信息。
UserMapper.java
@Mapper
public interface UserMapper {
@Insert("insert into users(user_id) values(#{userID})")
public void create(User user);
@Results(id = "userResult", value = {
@Result(property = "userID", column = "user_id", id = true),
@Result(property = "user_name", column = "userName"),
@Result(property = "password", column = "password")
})
@Select("select * from users where user_id = #{userID}")
public User selectUser(Integer userID);
}}
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>
......
<mappers>
<package name="com.demo.mybatis.step05.dao" />
</mappers>
</configuration>
标签:语句,缓存,映射,MyBatis,详解,user,Mybatis,id
From: https://blog.csdn.net/happysmooth/article/details/140998226