一、Hibernate基本介绍
1、产生原因
-
JDBC使用大致步骤
- 连接数据库,注册驱动和数据库信息
- 操作Connection,打开Statement对象
- 通过Statement执行SQL,返回结果到ResultSet对象
- 使用ResultSet读取数据,然后通过代码转化为具体的POJO对象
- 关闭数据库相关资源
-
由于JDBC操作过于复杂繁琐,于是就产生了对象关系映射(Objec Relational Mapping,简称ORM,OR mapping)
2、作用
- ORM模型就是数据库的表和简单java对象的映射关系模型,主要解决数据库数据和POJO对象的映射。通过映射关系就可以简单迅速地把数据库表的数据转化为POJO
3、优点
- 一个会话中,不需要操作多个对象,只需要操作Session对象即可
- 关闭资源也只需要关闭一个Session对象
4、缺点
- 全表映射带来的不便,如更新需要发送所有的字段
- 无法根据不同的条件组装不同的SQL
- 对关联和复杂SQL查询支持较差,需要自己写SQL,返回后,需要自己将数据组装为POJO
- 不能有效支持存储过程
- SQL性能较差时,不能做优化
二、MyBatis基本介绍
1、产生原因
- 为了解决Hibernate的不足,产生了一个半自动映射的框架Mybatis
2、半自动映射
- Mybatis需要手工提供POJO、SQL和映射关系,而全表映射的Hibernate只需要提供POJO和映射关系便可
<!-- POJO和映射关系 -->
<class name ="com.example.po.User" table = "user">
<id name = "id" type = "long">
<column name = "id" />
</id>
<property name = "userName" type = "string">
<column name = "user_name" length = "32"/>
</property>
</class>
3、基本构成
- SqlSessionFactoryBuilder(构造器)
- 根据配置信息或代码来生成SqlSessionFactory(工厂接口)
- SqlSessionFactory
- 依赖工厂来生成SqlSession(会话)
- SqlSession
- 既可以发送SQL去执行并返回结果,也可以获取Mapper的接口
- SQL Mapper
- Mybatis新设计的组件,它是由一个java接口和XML文件(或注解)构成,需要给出对象的SQL和映射规则。负责发送SQL去执行,并返回结果
1、构建SqlSessionFactory
- 每个Mybatis的应用都是以SqlSessionFactory的实例为中心,该实例可通过SqlSessionFactoryBuilder获得
- SqlSessionFactory是一个工厂接口而不是具体实现类,它的任务是创建SqlSession
- Mybatis提供的两个实现类:
- DefaultSqlSessionFactory(默认实现)
- SqlSessionManager
- Mybatis提供的两个实现类:
- Mybatis提供了两种模式创建SqlSessionFactory
- XML配置的方式(推荐使用,避免硬编码)
- 代码方式
Ⅰ、XML构建
- Mybatis解析mybatis-config.xml配置文件到Configuration对象里,并利用SqlSessionFactoryBuilder来创建SqlSessionFactory
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>
<!-- 环境 -->
<environments default="development">
<!-- 环境变量 -->
<environment id="development">
<!-- 事务管理器 -->
<transactionManager type="JDBC">
<property name="autoCommit" value="false"/>
</transactionManager>
<!-- 数据源 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!-- 映射器 -->
<mappers>
<mapper resource="com/sound/code/mybatis/mapper/roleMapper.xml"/>
</mappers>
</configuration>
构建SqlSessionFactory
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(inputStream);
Ⅱ、代码构建
- 代码构建修改配置信息后需要重新编译代码,不利于维护
public static SqlSessionFactory codeBuildSqlSessionFactory() {
// 数据库连接池
PooledDataSource dataSource = new PooledDataSource();
dataSource.setDriver("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC");
dataSource.setUsername("root");
dataSource.setPassword("123456");
// 数据库事务方式
TransactionFactory transactionFactory = new JdbcTransactionFactory();
// 运行环境
Environment environment = new Environment("development", transactionFactory, dataSource);
// Configuration对象
Configuration configuration = new Configuration(environment);
// 注册一个mybatis上下文别名
configuration.getTypeAliasRegistry().registerAlias("role", Role.class);
// 加入一个映射器
configuration.addMapper(RoleMapper.class);
// 使用configuration对象构建sqlSessionFactory = DefaultSqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
return sqlSessionFactory;
}
2、Configuration类
- 全限定类名:org.apache.ibatis.session.Configuration
- Configuration类存在于整个Mybatis应用的生命周期,便于重复读取和运用
- Configuration类保存着XML等Mybatis配置信息
3、构建SqlSession
Ⅰ、作用
- 获取映射器,让映射器通过命名空间和方法名称找到对应的SQL,发送给数据库执行后返回结果
- 直接通过命名信息去执行SQL返回结果。在SqlSession层可以通过CRUD的方法,带上SQL的id来操作XML中配置好的SQL。
- 通过commit、rollback方法提交或者回滚事务
4、映射器
- 映射器是由java接口和XML文件(或者注解)共同组成
Ⅰ、作用
- 定义参数类型
- 描述缓存
- 描述SQL语句
- 定义查询结果和POJO映射关系
Ⅱ、XML文件实现Mapper
接口
public interface RoleMapper {
Role getRole(Long id);
int insertRole(Role role);
int deleteRole(Long id);
}
XML文件
- 该文件配置在mybatis-config.xml中,Mybatis就会读取该XML配置文件生成映射器
- namespace命名空间对应接口全限定名称,SQL语句id对应接口方法名称
<?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.sound.code.mybatis.mapper.RoleMapper">
<select id="getRole" parameterType="long" resultType="role">
select id, role_name as roleName, note
from t_role
where id = #{id}
</select>
<insert id="insertRole" parameterType="role">
insert into t_role(role_name, note)
values (#{roleName}, #{note})
</insert>
<delete id="deleteRole" parameterType="long">
delete from t_role where id = #{id}
</delete>
</mapper>
4、生命周期
1、SqlSessionFactoryBuilder
- 只应存在于方法的局部
- 它是根据XML配置或者Java编码获取的资源来构建SqlSessionFactory的。其作用就是生成SqlSessionFactory,构建完成作用也就随之结束。
2、SqlSessionFactory(单例)
- 存在于Mybatis应用的整个生命周期
- 每次需要访问数据库就要通过SqlSessionFactory创建SqlSession
3、SqlSession
- 存在于请求数据库事务的过程中
- 是一个会话,相当于JDBC的一个Connection对象,每次使用完毕需要关闭
4、Mapper
- 生命周期同SqlSession
三、配置
1、mybatis配置信息
- XML文件的层测结构不能颠倒顺序,否则mybatsi解析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></properties>
<!-- 设置 -->
<settings></settings>
<!-- 类别名 -->
<typeAliases>
<typeAlias alias="role" type="com.sound.code.mybatis.po.Role"/>
</typeAliases>
<!-- 类型处理器 -->
<typeHandlers></typeHandlers>
<!-- 对象工厂 -->
<objectFactory type=""></objectFactory>
<!-- 插件 -->
<plugins>
<plugin interceptor=""></plugin>
</plugins>
<!-- 环境 -->
<environments default="development">
<!-- 环境变量 -->
<environment id="development">
<!-- 事务管理器 -->
<transactionManager type="JDBC">
<property name="autoCommit" value="false"/>
</transactionManager>
<!-- 数据源 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!-- 数据库厂商标识 -->
<databaseIdProvider type=""></databaseIdProvider>
<!-- 映射器 -->
<mappers>
<!-- 加入一个 -->
<mapper resource="com/sound/code/mybatis/mapper/roleMapper.xml"/>
<!-- 包扫描,多个 -->
<!-- <package name="com.sound.code.mybatis.mapper"/> -->
</mappers>
</configuration>
2、配置详解
Ⅰ、properties元素
- properties是一个配置属性的元素,让我们能在配置文件的上下文中使用它
- mybatis提供3种配置方式
- property子元素
- properties配置文件(首选该方式)
- 程序参数传递(可对读取的属性进行特殊处理)
- 3种配置方式的读取顺序
- 首先读取properties元素体内指定的property子元素
- 然后读取properties元素resource指定的外部properties配置文件,并覆盖已读取的同名属性
- 最后读取程序中Properties类中传递的属性,并覆盖已读取的同名属性
- 3中配置方式的优先级
- 程序传参优先级最高,外部配置文件次之,子元素优先级最低
- 不要使用混合方式(配置管理混乱)
property子元素
- 可在配置文件上下文中使用表达式使用配置信息
<!-- 属性 -->
<properties>
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</properties>
<!-- 数据源 -->
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
properties配置文件
#dataSource.properties文件
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
username=root
password=123456
<!-- 配置文件中引入外部properties文件,mybatis配置文件上下文中就可通过表达式使用 -->
<properties resource="dataSource.properties"></properties>
程序参数传递
public static SqlSessionFactory initSqlSessionFactory() {
String resource = "mybatis-config.xml";
InputStream inputStream = null;
Properties properties = new Properties();
try {
inputStream = Resources.getResourceAsStream(resource);
Base64.Decoder decoder = Base64.getDecoder();
properties.load(inputStream);
// 对数据库密码解密
properties.setProperty("password", new String(decoder.decode(properties.getProperty("username"))));
} catch (Exception e) {
Logger.getLogger(SqlSessionFactoryUtil.class.getName()).log(Level.SEVERE, null, e);
}
synchronized (CLASS_LOCK) {
if (sqlSessionFactory == null) {
// 使用properties中的值
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream, properties);
}
}
return sqlSessionFactory;
}
Ⅱ、设置
-
在 MyBatis 中 settings 是最复杂的配置,它能深刻影响 MyBatis 底层的运行,但是在大部分情况下使用默认值便可以运行,所以在大部分情况下不需要大量配置它,只需要修改一些常用的规则即可,比如自动映射、驼峰命名映射、级联规则、是否启动缓存、执行器(Executor)类型等
配置项 作用 配置选项 默认值 cacheEnabled 该配置影响所有映射器中配置缓存的全局开关 true/false true lazyLoadingEnabled 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。在特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态 true/false false aggressiveLazyLoading 当启用时,对任意延迟属性的调用会使带有延迟加载属性的对象完整加载;反之,每种属性将会按需加载 true/felse 版本3.4.1 (不包含)之前 true,之后 false multipleResultSetsEnabled 是否允许单一语句返回多结果集(需要兼容驱动) true/false true useColumnLabel 使用列标签代替列名。不同的驱动会有不同的表现,具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果 true/false true useGeneratedKeys 允许JDBC 支持自动生成主键,需要驱动兼容。如果设置为 true,则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍可正常工作(比如 Derby) true/false false autoMappingBehavior 指定 MyBatis 应如何自动映射列到字段或属性。
NONE 表示取消自动映射。
PARTIAL 表示只会自动映射,没有定义嵌套结果集和映射结果集。
FULL 会自动映射任意复杂的结果集(无论是否嵌套)NONE、PARTIAL、FULL PARTIAL autoMappingUnkno wnColumnBehavior 指定自动映射当中未知列(或未知属性类型)时的行为。 默认是不处理,只有当日志级别达到 WARN 级别或者以下,才会显示相关日志,如果处理失败会抛出 SqlSessionException 异常 NONE、WARNING、FAILING NONE defaultExecutorType 配置默认的执行器。SIMPLE 是普通的执行器;REUSE 会重用预处理语句(prepared statements);BATCH 执行器将重用语句并执行批量更新 SIMPLE、REUSE、BATCH SIMPLE defaultStatementTimeout 设置超时时间,它决定驱动等待数据库响应的秒数 任何正整数 Not Set (null) defaultFetchSize 设置数据库驱动程序默认返回的条数限制,此参数可以重新设置 任何正整数 Not Set (null) safeRowBoundsEnabled 允许在嵌套语句中使用分页(RowBounds)。如果允许,设置 false true false mapUnderscoreToCamelCase 是否开启自动驼峰命名规则映射,即从经典数据库列名 a_column 到经典 Java 属性名 aColumn 的类似映射 true false localCacheScope MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速联复嵌套査询。默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlScssion 的不同调用将不会共享数据 SESSION/STATEMENT SESSION jdbcTypeForNull 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER NULL、VARCHAR、OTHER OTHER lazyLoadTriggerMethods 指定哪个对象的方法触发一次延迟加载 — equals、clone、hashCode、toString defaultScriptingLanguage 指定动态 SQL 生成的默认语言 — org.apache.ibatis .script.ing.xmltags .XMLDynamicLanguageDriver callSettersOnNulls 指定当结果集中值为 null 时,是否调用映射对象的 setter(map 对象时为 put)方法,这对于 Map.kcySet() 依赖或 null 值初始化时是有用的。注意,基本类型(int、boolean 等)不能设置成 null true/false false logPrefix 指定 MyBatis 增加到日志名称的前缀 任何字符串 Not set proxyFactory 指定 MyBatis 创建具有延迟加栽能力的对象所用到的代理工具 CGLIB/JAVASSIST JAVASSIST (MyBatis 版本为 3.3 及以上的) vfsImpl 指定 VFS 的实现类 提供 VFS 类的全限定名,如果存在多个,可以使用逗号分隔 Not set useActualParamName 允许用方法参数中声明的实际名称引用参数。要使用此功能,项目必须被编译为 Java 8 参数的选择。(从版本 3.4.1 开始可以使用) true/false true
Ⅲ、别名
- 由于类的全限定名太长,可使用别名代指,并在mybatis上下文(配置文件、mapper.xml文件)中使用
- Mybatis中别名分为:系统定义别名、自定义别名
- Mybatis中别名不区分大小写
- 一个typeAliases的实例是在解析配置文件时生成的,并长期保存在Configuration对象中
系统别名
-
org.apache.ibatis.type.TypeAliasRegistry类的构造方法中定义了所有系统别名,并缓存在了map中
-
基本数据类型、基本数据包装类型、基本类型数组、基本数据包装类型数组
- 基本类型、基本数据包装类型都指出数组,即别名后加中括号
别名 | 映射类型 | 数组别名 |
---|---|---|
_int | int | _int[] |
int | Integer | int[] |
date | Date | date[] |
decimal | BigDecimal | decimal[] |
bigDecimal | BigDecimal | decimal[] |
object | Object | object[] |
string | String | 无 |
map | Map | 无 |
hashmap | Hashmap | 无 |
list | List | 无 |
arraylist | ArrayList | 无 |
collection | Collection | 无 |
iterator | Iterator | 无 |
ResultSet | ResultSet | 无 |
自定义别名
- 包扫描需配置@Alias(“别名”)类注解,若扫描包的类上没有该注解会将类名首字母小写作为别名,需注意重名的场景
<!-- 单一自定义别名 -->
<typeAliases>
<typeAlias alias="role" type="com.sound.code.mybatis.po.Role"/>
</typeAliases>
<!-- 包扫描 -->
<typeAliases>
<package name="com.sound.code.mybatis.po"/>
</typeAliases>
Ⅳ、typeHandler类型处理器
- Mybatis在预处理语句(PreparedStatement)中设置一个参数,或从结果集(ResultSet)中取一个值时,都会使用注册了的typeHandler处理
- typeHandler常用的配置为Java类型(javaType)、JDBC类型(jdbcType)。
- typeHandler的作用就是将参数从javaType转为jdbcType,将返回结果从jdbcType转为JavaType
自定义类型处理器
1、编码
public enum Sex {
MALE(5, "男"), FEMALE(8, "女");
private Integer code;
private String name;
Sex(Integer code, String name) {
this.code = code;
this.name = name;
}
// 省略get、set方法
public static Sex getSex(int id) {
Map<Integer, Sex> map = Arrays.asList(values()).stream().collect(Collectors.toMap(Sex::getCode, Function.identity()));
return map.get(id);
}
}
// 自定义枚举类型处理器
public class SexEnumTypeHandler extends BaseTypeHandler<Sex> {
// 入库时存入枚举的序列号
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Sex parameter, JdbcType jdbcType) throws SQLException {
ps.setInt(i, parameter.getCode());
}
// 获取枚举
@Override
public Sex getNullableResult(ResultSet rs, String columnName) throws SQLException {
int id = rs.getInt(columnName);
return Sex.getSex(id);
}
@Override
public Sex getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
int id = rs.getInt(columnIndex);
return Sex.getSex(id);
}
@Override
public Sex getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return null;
}
}
2、注册自定义类型处理器
- 必须将自定义的类型处理器注册到mybatis-config.xml文件中才能在上下文中使用
<!-- 类型处理器 -->
<typeHandlers>
<!-- javaType为枚举类,handler为自定义类型处理器 -->
<typeHandler handler="com.sound.code.mybatis.typeHandler.SexEnumTypeHandler" javaType="com.sound.code.mybatis.contant.Sex"/>
</typeHandlers>
- 使用示例
public class Role {
private Long id;
// PO中直接依赖枚举
private Sex sex;
}
<resultMap id="roleMap" type="role">
<id column="id" property="id"/>
<result column="sex" property="sex" typeHandler="com.sound.code.mybatis.typeHandler.SexEnumTypeHandler"/>
</resultMap>
<!-- 根据数据库sex列可直接查询出Sex枚举类到PO -->
<select id="getRole" parameterType="long" resultMap="roleMap">
select id, role_name as roleName, note, sex
from t_role
where id = #{id}
</select>
<!-- 可将枚举按照自定义规则存入数据库 -->
<insert id="insertRole" parameterType="role">
insert into t_role(role_name, note, sex)
values (#{roleName}, #{note}, #{sex})
</insert>
Ⅴ、environments配置环境
- 配置环境可以注册多个数据源( dataSource),每一个数据源分为两大部分:一个是数据库源的配置,另外一个是数据库事务( transactionManager)的配置
<!-- 环境 -->
<environments default="development">
<!-- 环境变量 -->
<environment id="development">
<!-- 事务管理器 -->
<transactionManager type="JDBC">
<property name="autoCommit" value="false"/>
</transactionManager>
<!-- 数据源 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
-
environments中的属性default,标明在缺省的情况下,我们将启用哪个数据源配置
-
environment元素是配置一个数据源的开始,属性id是设置这个数据源的标志,以便 MyBatis上下文使用它
-
transactionManager配置的是数据库事务,其中type属性有3种配置方式
- JDBC,采用JDBC方式管理事务,在独立编码中我们常常使用
- MANAGED,采用容器方式管理事务,例如 Spring。在JNDI数据源中常用。
- 自定义,由使用者自定义数据库事务管理办法,适用于特殊应用
-
transactionManager中的property元素是配置数据源的各类属性,我们这里配置了 autoCommit= false则是要求数据源事务不自动提交
-
dataSource标签,是配置数据源连接的信息,type属性是提供我们对数据库连接方式的配置
- UNPOOLED:表示每次都会开启和关闭连接, 不使用连接池技术
- POOLED:示采用连接池技术
- JNDI:使用其他容器(例如 Spring)提供数据源
- 自定义数据源
-
dataSource标签中的 property元素,就是定义数据库的各类参数(driver, url, username, password)
数据库事务
- 数据库事务MyBatis是交由 Sqlsession去控制的,我们可以通过 Sqlsession提交(commit)或者回滚( rollback)。我们插入一个角色对象,如果成功就提交,否则就回滚
public static void main(String[] args) {
SqlSession sqlSession = null;
try {
sqlSession = SqlSessionFactoryUtil.openSqlSession();
RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
Role role = new Role();
role.setRoleName("sdf");
role.setNote("fdsa");
role.setSex(Sex.MALE);
roleMapper.insertRole(role);
roleMapper.deleteRole(1L);
// 以上事务同时提交
sqlSession.commit();
} catch (Exception e) {
System.out.println(e.getMessage());
sqlSession.rollback();
} finally {
if (Objects.nonNull(sqlSession)) {
sqlSession.close();
}
}
数据源
MyBatis内部为我们提供了3种数据源的实现方式,即数据源的属性type取值
-
UNPOOLED,非连接池,使用 MyBatis提供的 org.apache ibatis datasource unpooledUnpooledData Source实现。
-
POOLED,连接池,使用 MyBatis提供的 org. apache ibatis datasource pooled. PooledDataSource实现。
-
JNDI,使用 My Batis提供的 org.apache ibatis datasource jndi. JndiDataSourceFactory来获取数据源
若需要使用自定义数据源,必须去实现 org.apacheibatisdatasource. DataSourceFactory接口
四、映射器
1、引入映射器
<!-- 文件路径引入 -->
<mappers>
<mapper resource="com/sound/code/mybatis/mapper/roleMapper.xml"/>
</mappers>
<!-- mapper接口引入 -->
<mappers>
<mapper class="com.sound.code.mybatis.mapper.RoleMapper"/>
</mappers>
<!-- 文件URL引入 -->
<mappers>
<mapper url="file:///var/mappers/com/sound/code/mybatis/mapper/roleMapper.xml"/>
</mappers>
<!-- 包扫描引入多个映射器-->
<mappers>
<package name="com.sound.code.mybatis.mapper"/>
</mappers>
2、映射器配置
元素名称 | 描述 | 备注 |
---|---|---|
selet | 查询语句,最常用、最复杂的元素之一 | 可以自定义参数,返回结果集等 |
insert | 插入语句 | 执行后返回一个整数,代表插入的条数 |
update | 更新语句 | 执行后返回一个整数,代表更新的条数 |
delet | 删除语句 | 执行后返回一个整数,代表删除的条数 |
parameterMap | 定义参数映射关系 | 即将被删除的元素,不建议大家使用 |
sql | 允许定义一部分的 SQL,然后在各个地方引用 | 例如,一张表列名,我们可以一次定义,在多个SQL 语句中使用它 |
reutMap | 用来描述从数据库结果集中来加载对象,它是最复杂、最强大的元素 | 它将提供映射规则 |
cache | 给定命名空间的缓存配置 | |
cache- ref | 其他命名空间缓存配置的引用 |
3、select元素
Ⅰ、自动映射
- 在配置文件中设置settings
- autoMappingBehavior:属性值来设置自动映射策略,取值参考配置表格
- mapUnderscoreToCamelCase:若数据库是规范命名的,即每一个单词都用下划线分隔,POJO 采用驼峰式命名方法,可将该配置设为 true,就可实现从数据库到 POJO的自动映射了
Ⅱ、传递参数
- 使用 Map 传递参数。因为 Map 导致业务可读性的丧失,从而导致后续扩展和维护 的困难,我们应该在实际的应用中果断废弃这样的传递参数的方式。
- 使用@Param注解传递多个参数,这种方式的使用受到参数个数(n)的影响。当n ≤5 时,它是最佳的传参方式,它比用 JavaBean 更好,因为它更加直观;当 n>5时,多个参数将给调用带来困难。
- 当参数个数多于 5个时,建议使用 JavaBean 方式。
4、insert元素
主键回填
- 插入数据后我们有时需要获得主键插入的主键,以便于未来的操作
<!-- keyProperty属性指定哪个是主键字段,useGeneratedKeys属性告诉MyBatis主键是否使用数据库内置策略生成 -->
<insert id="insertRole" parameterType="role" keyProperty="id" useGeneratedKeys="true">
insert into t_role(role_name, note, sex)
values (#{roleName}, #{note}, #{sex})
</insert>
代码中获取新插入数据的id值
Role role = new Role();
role.setRoleName("sdf");
role.setNote("fdsa");
role.setSex(Sex.MALE);
roleMapper.insertRole(role);
sqlSession.commit();
// 可直接获取回填的id值
Long id = role.getId();
自定义主键规则
<insert id="insertRole" parameterType="role" keyProperty="id" useGeneratedKeys="true">
<!-- keyProperty为主键字段,resultType为主键对应的pojo属性类型,sql为生成规则 -->
<selectKey keyProperty="id" resultType="long" order="BEFORE">
select if(max(id)is null,1,max(id)+ 2)as newId from t_role
</selectKey>
insert into t_role(id,role_name, note, sex)
values (#{id}, #{roleName}, #{note}, #{sex})
</insert>
5、特殊字符串替换和处理(#和$)
- 在 MyBatis 中,我们常常传递字符串,我们设置的参数#{name}在大部分的情况下 MyBatis 会用创建预编译的语句,然后 MyBatis 为它设值,而有时候我们需要的是传递 SQL语句的本身,而不是 SQL 所需要的参数
- 例如,在一些动态表格(有时候经常遇到根据不同的条件产生不同的动态列)中,我们要传递 SOL 的列名给 SQL的场景,当然 MyBatis 也对这样的场景进行了支持,这些是 Hibernate 难以做到的
- 例如,在程序中传递变量 columns="col1,col2,col3….“给 SQL,让其组装成为 SQL语句。我们当然不想被 MyBatis 像处理普通参数一样把它设为"coll,col2,col3….”,那么我们就可以写成如下语句
- select ${columns) from tablename 这样 MyBatis 就不会帮我们转译 columns,而不是作为 SQL 的参数进行设置了。只是这样是对 SQL 而言是不安全的,MyBatis 给了你灵活性的同时,也需要你自己去控制参数以保证 SQL 运转的正确性和安全性。
6、ResultMap
- 定义一个结果集的映射关系
<!-- 包含以下元素 -->
<resultMap>
<constructor>
<idArg/>
<arg/>
</constructor>
<id/>
<result/>
<association/>
<collection/>
<discriminator>
<case></case>
</discriminator>
</resultMap>
Ⅰ、constructor构造方法
- 其中 constructor 元素用于配置构造方法。一个POJO 可能没有无参构造方法,这个时候我们就可以使用constructor 进行配置。
<resultMap>
<constructor>
<!-- 数据库主键字段 -->
<idArg column="id" jdbcType="BIGINT" javaType="long"/>
<!-- 数据库其他字段 -->
<arg column="role_name" jdbcType="VARCHAR" javaType="string"/>
</constructor>
</resultMap>
Ⅱ、级联
- association,代表一对一关系,比如中国公民和身份证是一对一的关系。
- collection,代表一对多关系,比如班级和学生是一对多的关系,一个班级可以有多
- discriminator,是鉴别器,它可以根据实际选择采用哪个类作为实例,允许你根据特
定的条件去关联不同的结果集。
- 人有男人和女人。你可以实例化一个人的对象,但是要根据情况用男人类或者用女人类去实例化
一对一association
- property:指定映射到实体类的对象属性
- column:指定表中对应的字段(即查询返回的列名)
- javaType:指定映射到实体对象属性的类型
- select:指定引入嵌套查询的子 SQL 语句,该属性用于关联映射中的嵌套查询
方式一(嵌套查询)
public class Role {
private Long id;
private String roleName;
private RoleCard roleCard;
// 省略set、get方法
}
public class RoleCard {
private long id;
private String code;
// 省略set、get方法
}
public interface RoleMapper {
Role getRole(Long id);
}
public interface RoleCardMapper {
RoleCard getRoleCardByRoleId(long roleId);
}
<!-- 定义roleCardMapper的映射关系 -->
<select id="getRoleCardByRoleId" parameterType="long" resultType="org.example.mybatis.po.RoleCard">
select id, code, role_id
from t_role_card
where role_id = #{roleId}
</select>
<!-- 定义roleMapper的映射关系,将getRole查询的结果id,作为参数传递给getRoleCardByRoleId -->
<resultMap id="roleMap" type="role">
<id column="id" property="id"/>
<result column="role_name" property="roleName" javaType="string" jdbcType="VARCHAR"/>
<!-- property为Role属性映射,column是作为参数的查询结果,select是接收column的sql语句-->
<association property="roleCard" column="id" javaType="org.example.mybatis.po.RoleCard" select="org.example.mybatis.mapper.RoleCardMapper.getRoleCardByRoleId"/>
</resultMap>
<select id="getRole" parameterType="long" resultMap="roleMap">
select id, role_name, note, sex
from t_role
where id = #{id}
</select>
方式二(嵌套结果)
<resultMap id="getRoleAndCardByIdResult" type="role">
<id column="id" property="id"/>
<result column="role_name" property="roleName" javaType="string" jdbcType="VARCHAR"/>
<result column="note" property="note" jdbcType="VARCHAR"/>
<result column="sex" property="sex" typeHandler="org.example.mybatis.typeHandler.SexEnumTypeHandler"/>
<!-- 将关联查询的字段直接映射在该实体属性roleCard上 -->
<association property="roleCard" javaType="org.example.mybatis.po.RoleCard">
<id property="id" column="cardId"></id>
<result property="code" column="code"></result>
</association>
</resultMap>
<select id="getRoleAndCardById" parameterType="long" resultMap="getRoleAndCardByIdResult">
SELECT
tr.id,
tr.note,
tr.role_name,
tr.sex,
rc.id cardId,
rc.`code`
FROM
t_role tr
INNER JOIN t_role_card rc ON tr.id = rc.role_id
WHERE
tr.id = #{id}
</select>
一对多collection
- ofType:表示集合中的元素类型
- property:指定映射到实体类的对象属性
- column:指定表中对应的字段(即查询返回的列名)
- select:指定引入嵌套查询的子 SQL 语句,该属性用于关联映射中的嵌套查询
方式一(嵌套查询)
- 一个用户多个角色
<!-- RoleMapper.xml -->
<select id="getRoleByUid" parameterType="long" resultMap="getRoleByUidResultMap">
select id, role_name, note, sex, uid
from t_role
where uid = #{uid}
</select>
<!-- UserMapper.xml -->
<resultMap id="findUserAndRoleByIdResultMap" type="org.example.mybatis.po.User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="password" column="password"/>
<result property="age" column="age"/>
<result property="pw" column="pw"/>
<result property="driverAge" column="driver_age"/>
<!-- 一对多级联查询,ofType表示集合中的元素类型,将id传递给getRoleByUid -->
<collection property="roleList" ofType="org.example.mybatis.po.Role" column="id"
select="org.example.mybatis.mapper.RoleMapper.getRoleByUid"/>
</resultMap>
<select id="findUserAndRoleById" parameterType="long" resultMap="findUserAndRoleByIdResultMap">
SELECT u.id,
u.username,
u.`password`,
u.age,
u.pw,
u.driver_age
FROM `user` u
WHERE u.id = #{id}
</select>
方式二(嵌套结果)
- 只执行一次SQL
<!--对多根据uid查询用户及其关联的订单信息:级联查询的第二种方法(嵌套结果) -->
<resultMap id="findUserAndRoleByUserIdResultMap" type="org.example.mybatis.po.User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="password" column="password"/>
<result property="age" column="age"/>
<result property="pw" column="pw"/>
<result property="driverAge" column="driver_age"/>
<!-- 对多级联查询,ofType表示集合中的元素类型 -->
<collection property="roleList" ofType="org.example.mybatis.po.Role">
<id property="id" column="roleId"/>
<result property="roleName" column="role_name"/>
<result property="uid" column="uid"/>
<!-- 嵌套结果中还可再嵌套其他查询 -->
<association property="roleCard" column="roleId" javaType="org.example.mybatis.po.RoleCard"
select="org.example.mybatis.mapper.RoleCardMapper.getRoleCardByRoleId"/>
</collection>
</resultMap>
<select id="findUserAndRoleByUserId" parameterType="long" resultMap="findUserAndRoleByUserIdResultMap">
SELECT u.id,
u.username,
u.`password`,
u.age,
u.pw,
u.driver_age,
r.id roleId,
r.role_name,
r.uid
FROM `user` u
LEFT JOIN t_role r ON u.id = r.uid
WHERE u.id = #{id}
</select>
discriminator鉴别器
<resultMap id="findUserBySexResultMap" type="org.example.mybatis.po.User">
<!-- 当findUserByEnable查询结果u.enable为1时使用findUserAndRoleByUserIdResultMap这个ResultMap对结果集映射,反之使用2 -->
<discriminator javaType="int" column="enable">
<case value="1" resultMap="findUserAndRoleByUserIdResultMap"></case>
<case value="2" resultMap="findUserAndRoleByUserIdResultMap1"></case>
</discriminator>
</resultMap>
<select id="findUserByEnable" parameterType="long" resultMap="findUserBySexResultMap">
SELECT u.id,
u.username,
u.`password`,
u.age,
u.pw,
u.driver_age,
u.enable,
r.id roleId,
r.role_name,
r.uid
FROM `user` u
LEFT JOIN t_role r ON u.id = r.uid
WHERE u.id = #{id}
</select>
Ⅲ、性能分析和 N+1 问题
嵌套查询的级联才会存在N+1问题
- 级联的优势是能够方便快捷地获取数据。比如用户和角色信息往往是最常用关联的信息,这个时候级联是完全有必要的。
- 多层关联时,建议超过三层关联时尽量少用级联,因为不仅用处不大,而且会造成复杂度的增加,不利于他人的理解和维护。
- 同时级联时也存在一些劣势。有时候我们并不需要获取所有的数据。例如,我只对学生课程和成绩感兴趣,我就不用取出学生证和健康情况表了。因为取出学生证和健康情况表不但没有意义,而且会多执行几条 SQL,导致性能下降。我们可以使用代码去取代它。
- 级联还有更严重的问题,假设查询role后,查询到4个role,那么会执行4次查询roleCard,这样会造成 SOL 执行过多导致性能下降,这就是 N+1的问题,为了解决这个问题我们应该考虑采用延迟加载的功能。
Ⅳ、延迟加载
- 为了处理 N+1 的问题,MyBatis 引入了延迟加载的功能
- 延迟加载功能的意义在于,一开始并不取出级联数据,只有当使用它了才发送 SQL 去取回数据
- 在 MyBatis 的配置中有两个全局的参数 lazyLoadingEnabled和 aggressiveLazy Loading
- lazyLoadingEnabled 的含义是是否开启延迟加载功能
- aggressiveLazyLoading 的含义是对任意延迟属性的调用会使带有延迟加载属性的对象完整加载,反之,每种属性将按需加载
7、缓存
- 特点是将数据保存在计算机内存中,在读取的时候无需再从磁盘读入,因此具备快速读取和使用的特点
- 缓存的关键在于存储内容访问的命中率:如果缓存命中率高,那么可以极大地提高系统的性能。如果缓存命中率很低,那么缓存就不存在使用的意义了
Ⅰ、系统缓存
一级缓存
- MyBatis 对缓存默认是开启一级缓存(一级缓存只是相对于同一个 SqlSession 而言)
- 在参数和 SQL 完全一样的情况下,使用同一个 SqlSession 对象调用同一个 Mapper 的方法,只执行一次 SQL,因为使用 SqlSession 第一次查询后,MyBatis 会将其放在缓存中,以后再查询的时候,如果没有声明需要刷新,并且缓存没超时的情况下, SqlSession 都只会取出当前缓存的数据,而不会再次发送 SQL 到数据库
- 如果使用的是不同的 SqlSesion 对象,因为不同的 SqlSession 都是相互隔离的,所以用相同的Mapper、参数和方法,它还是会再次发送 SOL 到数据库去执行,返回结果
// 两次查询只会执行一次SQL
SqlSession sqlSession = SqlSessionFactoryUtil.openSqlSession();
RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
Role role = roleMapper.getRole(45L);
System.out.println("role = " + role);
Role role1 = roleMapper.getRole(45L);
System.out.println("role1 = " + role1);
// 使用不同的Sqlsession,即使查询相同的sql也还是要执行
SqlSession sqlSession1 = SqlSessionFactoryUtil.openSqlSession();
RoleMapper roleMapper1 = sqlSession1.getMapper(RoleMapper.class);
Role role2 = roleMapper1.getRole(45L);
System.out.println("role2 = " + role2);
二级缓存
-
为了解决不同的 SqlSession 是相互隔离,需要配置二级缓存,使得缓存在 SqlSessionFactory 层面上能够提供给各个 SqlSession 对象共享。 而 SqlSessionFactory 层面上的二级缓存是不开启的,二级缓存的开启需要进行配置
-
实现二级缓存的时候,MyBatis 要求返回的 POJO 必须是可序列化的,也就是要求实现 Serializable接口
-
使用二级缓存,sqlsession调用了commit后才会生效
sqlSession = SqlSessionFactoryUtil.openSqlSession();
RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
Role role = roleMapper.getRole(45L);
System.out.println("role = " + role);
Role role1 = roleMapper.getRole(45L);
System.out.println("role1 = " + role1);
// 开启二级缓存只有执行commit后,非当前sqlsesion执行相同SQL才会使用缓存
sqlSession.commit();
SqlSession sqlSession1 = SqlSessionFactoryUtil.openSqlSession();
RoleMapper roleMapper1 = sqlSession1.getMapper(RoleMapper.class);
Role role2 = roleMapper1.getRole(45L);
System.out.println("role2 = " + role2);
开启二级缓存
<!-- 在需要缓存数据的mapper.xml中配置cache即可 -->
<mapper namespace="org.example.mybatis.mapper.RoleMapper">
<cache/>
</mapper>
开启后的默认值
- 文件中的所有 select 语句结果都将会被缓存
- 文件中的所有 insert、update 和 delete 语句都会刷新缓存
- 缓存会使用默认的Least Recently Used(LRU,最近最少使用的)算法策略来回收缓存
- 根据时间表,比如 No Flush Interval,(CNFI,没有刷新间隔),缓存不会以任何时 间顺序来刷新
- 缓存会存储列表集合或对象(无论查询方法返回什么)的 1024 个引用
- 缓存会被视为是 read/write(可读/可写)的缓存,意味着对象检索不是共享的,而 且可以安全地被调用者修改,不干扰其他调用者或线程所做的潜在修改
缓存策略
<!-- 在需要缓存数据的mapper.xml中配置cache即可 -->
<mapper namespace="org.example.mybatis.mapper.RoleMapper">
<cache eviction="LRU" flushInterval="100000" size="1024" readOnly="true"/>
</mapper>
- eviction∶缓存回收策略
- LRU,最近最少使用的,移除最长时间不用的对象
- FIFO,先进先出,按对象进入缓存的顺序来移除它们
- SOFT,软引用,移除基于垃圾回收器状态和软引用规则的对象
- WEAK,弱引用,更积极地移除基于垃圾收集器状态和弱引用规则的对象
- flushInterval∶刷新间隔时间,单位为毫秒。不配置时 SQL 被执行的时候才会去刷新缓存
- size∶ 缓存最多可以存储多少个对象。引用数目,一个正整数,不宜设置过大。 设置过大会导致内存溢出
- readOnly∶ 只读,意味着缓存数据只能读取而不能修改,优点可 快速读取缓存,缺点不能修改缓存,默认值为 false,不允许我们修改
- type:自定义缓存
Ⅱ、自定义缓存
- 系统缓存是 MyBatis 应用机器上的本地缓存,不支持集群、分布式缓存,此时可使用redis等缓存
Redis依赖
- 添加mybatis提供的reids缓存实现依赖
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-redis</artifactId>
<version>1.0.0-beta2</version>
</dependency>
Redis配置
- resources目录下添加redis.properties配置文件
host=192.168.8.29
port=6379
password=
timeout=5000
connectionTimeout=5000
soTimeout=5000
database=9
usePool=true
#redis pool configuration
maxTotal=600
maxIdle=300
minIdle=10
maxWaitMillis=2000
testOnBorrow=false
testOnReturn=falseb
开启自定义缓存
- 返回的 POJO 必须是可序列化的,实现 Serializable接口
<cache type="org.mybatis.caches.redis.RedisCache" eviction="LRU" size="1024"/>
五、动态SQL
1、元素
元素 | 作用 | 备注 |
---|---|---|
if | 判断语句 | 单条件分支判断 |
choose(when、otherwise) | 相当于Java中的case when语句 | 多条件分支判断 |
trim(where、set) | 辅助元素 | 用于处理一些SQL拼装问题 |
foreach | 循环语句 | 在in语句等列举条件常用 |
2、IF元素
- if 元素是我们最常用的判断语句,相当于 Java 中的 if 语句。它常常与test 属性联合使用。
- 不要使用“where 1=1 ”来拼接if的过滤条件
- 不影响SQL索引的使用
- 存在SQL 注入的风险
<select id="selectUserByIf" resultType="com.po.MyUser" parameterType="com.po.MyUser">
select * from user where 1=1
<if test="uname!=null and uname!=''">
and uname like concat('%',#{uname},'%')
</if >
<if test="usex !=null and usex !=''">
and usex=#{usex}
</if >
</select>
3、choose(when、otherwise)元素
- 类似 Java中的 switch 语句
<select id="selectUserByChoose" resultType="com.po.MyUser" parameterType= "com.po.MyUser">
select * from user 1 = 1
<choose>
<when test="uname!=null and uname!=''">
and uname like concat('%',#{uname},'%')
</when>
<when test="usex!=null and usex!=''">
and usex=#{usex}
</when>
<otherwise>
and uid > 10
</otherwise>
</choose>
</select>
4、trim元素
- prefix:给包含sql语句拼接前缀
- suffix:给包含sql语句拼接后缀
- prefixOverrides:去除包含sql语句头部由prefixOverrides指定的的关键字或者字符
- suffixOverrides:去除包含sql语句尾部由suffixOverrides指定的的关键字或者字符
<select id="selectUserByTrim" resultType="com.po.MyUser"parameterType="com.po.MyUser">
select * from user
<trim prefix="where" prefixOverrides = "and | or">
<if test="uname!=null and uname!=''">
and uname like concat('%',#{uname},'%')
</if>
<if test="usex!=null and usex!=''">
and usex=#{usex}
</if>
</trim>
</select>
5、Where元素
- where标签只会在包含的标签中有返回值的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where标签也会将它们忽略,在 where元素中不需要考虑空格的问题
<!--使用where元素根据条件动态查询用户信息-->
<select id="selectUserByWhere" resultType="com.po.MyUser" parameterType="com.po.MyUser">
select * from user
<where>
<if test="uname != null and uname ! = ''">
and uname like concat('%',#{uname},'%')
</if>
<!-- 这里多了个空格 -->
<if test="usex != null and usex != '' ">
and usex=#{usex}
</if >
</where>
</select>
6、Set元素
- 在动态 update 语句中可以使用set元素动态更新列
<!--使用set元素动态修改一个用户-->
<update id="updateUserBySet" parameterType="com.po.MyUser">
update user
<set>
<if test="uname!=null">uname=#{uname}</if>
<if test="usex!=null">usex=#{usex}</if>
</set>
where uid=#{uid}
</update>
7、foreach元素
-
collection 该属性是必选的,传递进来List、Set等集合参数名称
-
item 表示集合中每一个元素进行迭代时的别名
-
index 指定一个名字,用于表示在迭代过程中每次迭代到的下标位置
-
open 表示该语句以什么开始
-
separator 表示在每次进行迭代之间以什么符号作为分隔符
-
close 表示以什么结束
<!--使用foreach元素查询用户信息-->
<select id="selectUserByForeach" resultType="com.po.MyUser" parameterType=
"List">
select * from user where uid in
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
# {item}
</foreach>
</select>
Ⅰ、collection 属性
- 如果传入的是单参数且参数类型是一个 List,collection 属性值为 list。
- 如果传入的是单参数且参数类型是一个 array数组,collection 的属性值为 array。
- 如果传入的参数是多个,需要把它们封装成一个 Map,当然单参数也可以封装成 Map。Map 的 key 是参数名,collection 属性值是传入的 List 或 array 对象在自己封装的 Map 中的 key。
参数为list
- List myList = new ArrayList();
<select id="test" parameterType="java.util.List" resultType="User">
select * from user where id in
<foreach collection="myList" index="index" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</select>
参数为数据
- int[] ids = new int[3];
<select id="dynamicForeach2Test" parameterType="java.util.ArrayList" resultType="User">
select * from user where id in
<foreach collection="ids" index="index" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</select>
参数为封装的Map
- Map<String,Object> map = new HashMap<>();
<update id="updateBatch" parameterType="java.util.Map">
update user
<trim prefix="set" suffixOverrides=",">
<trim prefix="name =case" suffix="end,">
<foreach collection="map" item="i" index="index">
<if test="i.standardFromUuid!=null">
when id=#{i.id} then #{i.standardFromUuid}
</if>
</foreach>
</trim>
<trim prefix="standard_to_uuid =case" suffix="end,">
<foreach collection="map" item="i" index="index">
<if test="i.standardToUuid!=null">
when id=#{i.id} then #{i.standardToUuid}
</if>
</foreach>
</trim>
</trim>
where id in
<foreach collection="map" item="i" open="(" separator="," close=")">
#{i.id}
</foreach>
</update>
8、bind元素
- 通过 OGNL 表达式去自定义一个上下文变量
- MySOL 数据库,参数相连接用的是concat用"%",在 Oracle数据库则是用连接符号"|",这样 SQL 就需要提供两种形式去实现。但是有了bind元素,我们就完全不必使用数据库的语言,只要使用MyBatis的语言即可与所需参数相连
List<RoleBean> findRole(@Param("roleName")String roleName,@Param("note") String note);
<!-- bind自定义上下文变量 -->
<select id="findRole" resultType="com.learn.chapter5.mybatis.bean.RoleBean">
<bind name="pattern_roleName" value="'%'+ roleName +'%'"/>
<bind name="pattern_note" value="'%'+ note + '%'"/>
SELECT id,role_name as roleName,create_date as createDate, end_date as endFlag,end_flag as endFlag,note FROM
t_role where role_name like #{pattern_roleName} and note like #{pattern_note}
</select>