mybatis
MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装。MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录。
持久层框架,是一个不完全的ORM框架,sql语句需要程序员自己进行编写,mybatis有输入输出映射。
mybatis框架使用步骤:
1、配置mybatis配置文件,sqlMaprConfig.xml(名称不固定);
2、通过配置文件加载mybatis运行环境,创建sqlSessionFactory会话工厂(sqlSessionFactory实际中按照单例管理);
3、通过sqlSessionFactory创建sqlSession会话(sqlSession是面向用户的接口,实现对象是线程不安全的,建议sqlSession的使用场合是在方法体内);
4、调用sqlSession的方法去操作数据库(如果需要提交事务,则必须调用sqlSession的commit()方法);
1、Configuration(配置文件)
1-1、db.properties(数据库连接配置)
oracle
jdbc.driver=oracle.jdbc.driver.OracleDriver
jdbc.url=jdbc:oracle:@localhost:1521:orcl
jdbc.username=scott
jdbc.password=tiger
jdbc.driver=com.MysqL.jdbc.Driver
jdbc.url=jdbc:MysqL://localhost:3306/MysqL
jdbc.username=root
jdbc.password=root
1-2、sqlMapperConfig.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>
<!-- 加载外部的jdbc.properteis文件 -->
<properties resource="jdbc.properties"></properties>
<typeAliases>
<!--单个java类进行配置 -->
<typeAlias type="com.upinthewind.pojo.User"/>
<typeAlias type="com.upinthewind.pojo.Book"/>
<typeAlias type="com.upinthewind.util.PageBean"/>
<!-- 批量定义别名,指定包名,此时pojo类的别名是pojo类的类名首字母大写或小写都行 -->
<package name="com.upinthewind.dao"/>
<package name="com.upinthewind.pojo"/>
<package name="com.upinthewind.util"/>
</typeAliases>
<environments default="development">
<environment id="development">
<!-- 使用JDBC的事务管理方式 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 使用mybatis自己的连接池 -->
<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>
<!--加载sql映射文件-->
<mappers>
<!--批量注册 -->
<package name="com.upinthewind.dao"/>
<!--单个mapper注册-->
<mapper class="com.upinthewind.dao.UserDaoMapper"/>
<mapper resource="com/upinthewind/dap/UserDaoMapper.xml"/>
<mapper url=""/>
</mappers>
</configuration>
1-3、xxxMapper.xml(映射文件)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
为这个mapper指定一个唯一的namespace,namespace的值习惯上设置成包名+sql映射文件名,这样就能够保证namespace的值是唯一的例如:
namespace="me.gacl.mapping.userMapper"
就是me.gacl.mapping(包名)+userMapper(userMapper.xml文件去除后缀)
-->
<mapper namespace="me.gacl.mapping.userMapper">
<!-- 自定义返回结果集 -->
<resultMap type="com.upinthewind.pojo.Book" id="bookResultMap">
<!--
<id column="表的主键字段" jdbcType="字段类型" property="映射pojo对象的主键属性" />
<result column="表的字段" jdbcType="字段类型" property="映射到pojo对象的一个属性"/>
-->
<id column="bookno" property="bookno"/>
<result column="bookname" property="bookname"/>
<result column="author" property="author"/>
<result column="cps" property="cps"/>
<result column="cpdate" property="cpdate"/>
<result column="kucun" property="kucun"/>
<!--
association 用与映射关联查询的单个对象信息
property 要将关联查询的对象信息映射到book对象的哪个属性
<id column="主键字段" jdbcType="字段类型" property="集合中pojo对象的主键属性" />
<result column="表的字段" jdbcType="字段类型" property="集合中的pojo对象的属性" />
-->
<association property="booktype" javaType="com.upinthewind.pojo.BookType">
<id column="typeno" property="typeno"/>
<result column="typename" property="typename"/>
</association>
</resultMap>
<!-- 根据id查询得到一个user对象-->
<!--
id:"queryUser",id属性值必须是唯一的,不能够重复
parameterType:指明查询时使用的参数类型
resultType:指明查询返回的结果集类型,User类就是users表所对应的实体类
-->
<select id="queryUser" parameterType="int" resultType="com.upinthewind.pojo.User">
select * from users where id=#{id}
</select>
</mapper>
2、sqlSession(sql会话)
2-1、sqlSessionFactoryBuilder
通过sqlSessionFactoryBuilder创建sqlSessionFactory会话工厂
将sqlSessionFactoryBuilder当成工具使用即可,不必使用单例管理sqlSessionFactoryBuilder
在需要创建sqlSessionFactory时,只需要new一次sqlSessionFactoryBuilder即可
2-2、sqlSessionFactory
通过sqlSessionFactory创建sqlSession,使用单例管理sqlSessionFactory(工厂一创建,只使用一个实例)
mybatis和spring整合后,使用单例模式管理sqlSessionFactory
2-3、sqlSession
sqlSession是线程不安全的,在sqlSession实现类中除了有接口的方法(操作数据库方法),还有数据域属性
sqlSession最佳使用场合是在方法体中,定义成局部变量使用
public class MybatisUtil {
//饿汉设计模式
private static MybatisUtil mu = new MybatisUtil();
private sqlSessionFactory ssf;//会话工厂
/**
* 构造方法创建会话工厂对象
*/
private MybatisUtil() {
InputStream is = MybatisUtil.class.getClassLoader().getResourceAsstream("sqlMapperConfig.xml");
ssf = new sqlSessionFactoryBuilder().build(is);
}
/**
* 得到该单例模式的单例
* @return
*/
public static MybatisUtil init() {
return mu;
}
/**
* 得到与数据库的会话
* @return
*/
public sqlSession getsqlSession() {
return ssf.openSession();
}
}
3、sqlMapperConfig.xml(全局配置文件)
3-1、properties(属性)
将jdbc的连接参数单独配置在properties文件中,只需要在sqlMapperConfig.xml中加载properties中定义的属性值,在sqlMapperConfig中不需要对数据库连接参数进行硬编码。同时方便管理数据库连接参数。
特性:
属性加载顺序
1、在properties元素体内定义的属性优先被读取
2、然后读取properties中resource或者url引入的外部属性,会覆盖已存在的同名属性
3、最后读取parameterType传递的属性值,会覆盖同名属性
建议:
1、不要在properties中定义属性值,将属性定义在properties文件中
2、properties文件中的定义属性名要有一定的特殊性,如:jdbc.username
<!-- 引入外部db连接配置文件 -->
<properties resource="db.properties"></properties>
<!-- 也可以用property设置替换属性 -->
<properties>
<property name="url" value="jdbc:MysqL://localhost:3306/tangwenmingdb"/>
<property name="user" value="root"/>
<property name="password" value="root"/>
<property name="driverClass" value="com.MysqL.jdbc.Driver"/>
</properties>
3-2、settings(全局参数配置)
<settings>
<!-- 所有映射器中配置的缓存的全局开关。默认值true -->
<setting name="cacheEnabled" value="true" />
<!--延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。默认值false -->
<setting name="lazyLoadingEnabled" value="true" />
<!-- 是否允许单一语句返回多结果集(需要兼容驱动)。默认值true -->
<setting name="multipleResultSetsEnabled" value="true" />
<!-- 使用列标签代替列名。不同的驱动在这方面会有不同的表现,默认值true -->
<setting name="useColumnLabel" value="true" />
<!-- 允许 JDBC 支持自动生成主键,需要驱动兼容。默认值false -->
<setting name="useGeneratedKeys" value="false" />
<!--
指定 MyBatis 应如何自动映射列到字段或属性。
NONE 表示取消自动映射;
PARTIAL 只会自动映射没有定义嵌套结果集映射的结果集。
FULL 会自动映射任意复杂的结果集(无论是否嵌套)。
-->
<!-- 默认值PARTIAL -->
<setting name="autoMappingBehavior" value="PARTIAL" />
<setting name="autoMappingUnkNownColumnBehavior" value="WARNING" />
<!--
配置默认的执行器。
SIMPLE 就是普通的执行器;
REUSE 执行器会重用预处理语句(prepared statements);
BATCH 执行器将重用语句并执行批量更新。默认SIMPLE
-->
<setting name="defaultExecutorType" value="SIMPLE" />
<!-- 设置超时时间,它决定驱动等待数据库响应的秒数。 -->
<setting name="defaultStatementTimeout" value="25" />
<setting name="defaultFetchSize" value="100" />
<!-- 允许在嵌套语句中使用分页(RowBounds)默认值False -->
<setting name="safeRowBoundsEnabled" value="false" />
<!-- 是否开启自动驼峰命名规则映射,列名 A_COLUMN->属性名 aColumn的类似映射。 默认false -->
<setting name="mapUnderscoreToCamelCase" value="false" />
<!--
MyBatis利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。
默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。
若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 sqlSession的不同调用将不会共享数据。 -->
<setting name="localCacheScope" value="SESSION" />
<!--
当没有为参数提供特定的 JDBC 类型时,为空值指定JDBC类型。
某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。 -->
<setting name="jdbcTypeForNull" value="NULL" />
<!-- 指定哪个对象的方法触发一次延迟加载。 -->
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString" />
</settings>
3-3、typeAliases(别名)
在mapper中定义了很多的statement需要指定parameterType的类型,以及resultType指定的类型,写全路径不方便进行开发,则可以在mapper.xml中定义parameterType和resultType别名,方便开发。
别名 | 映射的类型 |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
map | Map |
自定义别名
type:类型的路径
alias:别名
单个定义别名
<typeAliases>
<!-- 这样定义后就可以用city来代替整个City类全路径了-->
<typeAlias alias="user" type="com.upinthewind.pojo.User"/>
</typeAliases>
批量定义别名
<typeAliases>
<!-- 批量定义别名,指定包名,此时pojo类的别名是pojo类的类名首字母大写或小写都行 -->
<package name="com.upinthewind.pojo"/>
</typeAliases>
3-4、typeHandlers(类型处理器)
mybatis的typeHandlers完成jdbc类型和java类型的转换
默认类型处理器
类型处理器 | Java 类型 | JDBC 类型 |
---|---|---|
BooleanTypeHandler | java.lang.Boolean, boolean | 数据库兼容的 BOOLEAN |
ByteTypeHandler | java.lang.Byte, byte | 数据库兼容的 NUMERIC 或 BYTE |
ShortTypeHandler | java.lang.Short, short | 数据库兼容的 NUMERIC 或 SHORT INTEGER |
IntegerTypeHandler | java.lang.Integer, int | 数据库兼容的 NUMERIC 或 INTEGER |
LongTypeHandler | java.lang.Long, long | 数据库兼容的 NUMERIC 或 LONG INTEGER |
FloatTypeHandler | java.lang.Float, float | 数据库兼容的 NUMERIC 或 FLOAT |
DoubleTypeHandler | java.lang.Double, double | 数据库兼容的 NUMERIC 或 DOUBLE |
BigDecimalTypeHandler | java.math.BigDecimal | 数据库兼容的 NUMERIC 或 DECIMAL |
StringTypeHandler | java.lang.String | CHAR, VARCHAR |
ClobReaderTypeHandler | java.io.Reader | - |
ClobTypeHandler | java.lang.String | CLOB, LONGVARCHAR |
NStringTypeHandler | java.lang.String | NVARCHAR, NCHAR |
NClobTypeHandler | java.lang.String | NCLOB |
BlobInputStreamTypeHandler | java.io.InputStream | - |
ByteArrayTypeHandler | byte[] | 数据库兼容的字节流类型 |
BlobTypeHandler | byte[] | BLOB, LONGVARBINARY |
DateTypeHandler | java.util.Date | TIMESTAMP |
DateOnlyTypeHandler | java.util.Date | DATE |
TimeOnlyTypeHandler | java.util.Date | TIME |
sqlTimestampTypeHandler | java.sql.Timestamp | TIMESTAMP |
sqlDateTypeHandler | java.sql.Date | DATE |
sqlTimeTypeHandler | java.sql.Time | TIME |
ObjectTypeHandler | Any | OTHER 或未指定类型 |
EnumTypeHandler | Enumeration Type | VARCHAR-任何兼容的字符串类型,存储枚举的名称(而不是索引) |
EnumOrdinalTypeHandler | Enumeration Type | 任何兼容的 NUMERIC 或 DOUBLE 类型,存储枚举的索引(而不是名称)。 |
自定义类型处理器
<update id="update" parameterType="twm.mybatisdemo.pojo.User">
update user set
username=#{username},
password=#{password},
address=#{address}
where id=#{id}
</update>
public class Address {
String province;
String city;
public Address() {}
public Address(String province, String city) {
this.province = province;
this.city = city;
}
//getter and setter......
}
问题:
pojo.User类中的address字段并不是String类型,而是一个自定义的Address
类型,
address字段需要传递给#{address}
,而address字段是Address类型的,MyBatis并不知道该怎样来处理这个类型的对象。因此,需要创建一个自定义的类型处理器(TypeHandler)了
步骤:
1、创建类型处理器
class AddresstypeHandler extends BaseTypeHandler<Address>{
//通过ide自动生成代码,可以看到父类BaseTypeHandler有四个方法需要我们实现
//包括三个get方法,一个set方法。
}
2、实现get方法
三个get方法都是用于将数据库获得的记录集里的address字段转成java Address类型的对象。
所以我们首先给Address类增加一个构造方法,用途:根据字符串生成一个实例对象。
//假设我们存储在db中的字符串是以","号分隔省市关系的
public Address(String address) {
if (address != null) {
String[] segments = address.split(",");
if (segments.length > 1) {
this.province = segments[0];
this.city = segments[1];
}
else if (segments.length > 0) {
this.city = segments[0];
}
}
}
然后实现AddresstypeHandler类中的三个get方法:
@Override
public Address getNullableResult(ResultSet rSet, String columnName)
throws sqlException {
return new Address(rSet.getString(columnName));
}
@Override
public Address getNullableResult(ResultSet rSet, int columnIndex)
throws sqlException {
return new Address(rSet.getString(columnIndex));
}
@Override
public Address getNullableResult(CallableStatement cStatement, int columnIndex)
throws sqlException {
return new Address(cStatement.getString(columnIndex));
}
3、实现set方法
set方法是用来将java类型转成数据库存储的类型。
这里我们先实现一下Address类的toString()方法(如果toString另有它用,那么就另外用一个方法名)
@Override
public String toString() {
return this.province + "," + this.city;
}
@Override
public void setNonNullParameter(PreparedStatement pStatement, int index,
Address address, JdbcType jdbcType) throws sqlException {
pStatement.setString(index, address.toString());
}
然后实现AddresstypeHandler类中的setNonNullParameter
<!-- 注册自定义类型处理器 -->
<typeHandlers>
<typeHandler handler="twm.mybatisdemo.type.AddresstypeHandler" />
</typeHandlers>
3-5、objectFactory(对象工厂)
3-6、plugins(插件)
3-7、environments(环境配置)
<environments default="development">
<environment id="development">
<!-- 使用JDBC的事务管理方式 -->
<transactionManager type="JDBC"></transactionManager>
<!-- 使用mybatis自己的连接池 -->
<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>
3-9、transactionManager(事务管理器)
mybatis提供了两种事务管理器,分别是:JDBC , MANAGED
JDBC:这个配置就是直接使用了JDBC 的提交和回滚设置,它依赖于从数据源得到的连接来管理事务范围。
MANAGED :这个配置从来都不提交和回滚一个连接,而是让容器来管理事务的整个生命周期(比如JEE应用服务的上下文)。默认情况下他会关闭连接,然而一些容器并不希望这样,因此需要将closeConnection属性设置为false来阻止它默认的关闭行为。
<transactionManager type="MANAGED">
<property name="closeConnection" value="false"/>
</transactionManager>
3-10、dataSource(数据源)
dataSource元素使用标准的JDBC数据源接口来配置JDBC连接对象的资源。mybatis提供了3种数据源类型,分别是:POOLED,UNPOOLED,JNDI
**UNPOOLED:**表示不支持数据源连接池。这样每次请求都会进行打开和关闭数据库连接操作,性能上会有些损耗。但对一些没有性能要求的程序来说使用起来较方便,配置少。
UNPOOLED 类型的数据源仅仅用来配置以下 5 种属性:
driver 这是 JDBC 驱动的 Java 类的完全限定名(如果你的驱动包含,它也不是 数据源类)。
url 这是数据库的 JDBC URL 地址。
username 登录数据库的用户名。
password 登录数据库的密码。
defaultTransactionIsolationLevel 默认的连接事务隔离级别。
**POOLED:**表示支持JDBC数据源连接池。这样可避免每次请求都要创建新的连接。
poolMaximumActiveConnections:任意时间的最大活动(正在使用)连接数量,默认值:10
poolMaximumIdleConnections :任意时间的最大空闲(没有使用)连接数。
poolMaximumCheckoutTime:再被强制返回之前,池中连接被checkout时间,默认值2W毫秒 即20s
poolTimetoWait:获取连接超时时间。默认:2W毫秒即20s。如果超时,它会给连接池打印状态日志并重新尝试获取
**JNDI:**表示支持外部数据源连接池。如mybatis与tomcat整合配置jndi
initial_context
用 来 从 初 始 上 下 文 中 寻 找 环 境 ( 也 就 是initialContext.lookup(initial——context)
这是个可选属性,如果被忽略,那么 data_source 属性将会直接以 initialContext 为背景再次寻找。 data_source
这是引用数据源实例位置的上下文的路径。
它会以由 initial_context 查询返回的环境为背景来查找
如果 initial_context 没有返回结果时,直接以初始 上下文为环境来查找
3-11、mappers(映射注册)
<!-- 第一种方式: 一个个指定接口文件及配置文件位置-->
<mappers>
<mapper class="com.upinthewind.dao.UserDaoMapper"/>
<mapper resource="com/upinthewind/dap/UserDaoMapper.xml"/>
<mapper url=""/>
</mappers>
<!-- 第二种方式: 指定包加载。可指定多个包 -->
<mappers>
<package name="com.upinthewind.dao" />
</mappers>
4、mapper(映射文件)
mybatis编写dao层:
4-1、原始dao层编写方法
需要程序员编写dao层的接口和实现类
需要在dao层的实现类中注入sqlSessionFactory工厂
4-2、mapper代理方式
只需要编写mapper接口(即dao层的接口)
编写mapper.xml(映射文件)和mapper.java(需遵循相应的开发规范)
开发规范:
(1)、在mapper.xml中namespace属性值为mapper接口全路径
<mapper namespace="me.gacl.mapping.userMapper">
(2)、mapper接口中的方法名要与mapper.xml中的statement的id一致
(3)、mapper接口中方法的输入参数要与mapper.xml对应的statement的parameterType的类型一致
(4)、mapper接口中方法的返回值要与mapper.xml对应的statement的resultType的类型一致
sqlSession ss = MybatisUtil.init().getsqlSession();//得到sqlSession
BookMapper bd = ss.getMapper(BookMapper.class);//mybatis动态创建接口的实现类
bd.deleteBookByBookNo(59);//调用方法
ss.commit();
ss.close();
5、动态sql
mybatis核心对sql语句进行灵活操作,通过表达式对sql语句进行判断、拼接、组装
5-1、where if
<!--多条件查总记录数 -->
<select id="queryAllRecords" parameterType="Condition" resultType="int">
select count(1) from tbl_book
<where>
<if test="bookName!=null and bookName!=''">
and bookName like concat('%',concat(#{bookName},'%'))
</if>
<if test="typeNo!=null and typeNo!=0">
and typeNo=#{typeNo}
</if>
<if test="beginTime!=null">
and cpDate > #{beginTime}
</if>
<if test="endTime!=null">
and cpDate < #{endTime}
</if>
</where>
</select>
5-2、choose, when, otherwise
不想应用到所有的条件语句,而只想从中择其一项
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>
5-3、if set
当 update 语句中没有使用 if 标签时,如果有一个参数为 null,都会导致错误。
当在 update 语句中使用if标签时,如果前面的if没有执行,则或导致逗号多余错误。使用set标签可以将动态的配置 SET 关键字,并剔除追加到条件末尾的任何不相关的逗号。使用 if+set 标签修改后,如果某项为 null 则不进行更新,而是保持数据库原值。
<!-- if/set(判断参数) - 将实体 User类不为空的属性更新 -->
<update id="updateUser_if_set" parameterType="com.pojo.User">
UPDATE user
<set>
<if test="username!= null and username != '' ">
username = #{username},
</if>
<if test="sex!= null and sex!= '' ">
sex = #{sex},
</if>
<if test="birthday != null ">
birthday = #{birthday},
</if>
</set>
WHERE user_id = #{userid};
</update>
5-4、foreach
处理传入参数为数组或list集合,使用foreach解析
mapper.xml
<!--
item表示集合中每一个元素进行迭代时的别名,
index指 定一个名字,用于表示在迭代过程中,每次迭代到的位置,
open表示该语句以什么开始,
separator表示在每次进行迭代之间以什么符号作为分隔 符,
close表示以什么结束。
-->
<select id="queryBuBookNos" parameterType="java.util.List" resultType="Book">
select * from tbl_book where bookNo in
<foreach collection="list" index="index" item="item_no" open="(" separator="," close=")" >
#{item_no}
</foreach>
</select>
<select id="queryBuBookNos" parameterType="java.util.Array" resultType="Book">
select * from tbl_book where bookNo in
<foreach collection="array" index="index" item="item_no" open="(" separator="," close=")" >
#{item_no}
</foreach>
</select>
mapper接口
List<Book> queryBuBookNos(List<Integer> list);
测试
@Test
public void queryBuBookNostest() {
sqlSession ss = MybatisUtil.init().getsqlSession();
BookMapper bd = ss.getMapper(BookMapper.class);
List<Integer> list = new ArrayList<>();
//226 95 102 103 104
list.add(226);
list.add(95);
list.add(102);
list.add(103);
list.add(104);
List<Book> books = bd.queryBuBookNos(list);
for (Iterator it = books.iterator(); it.hasNext();) {
Book book = (Book) it.next();
System.out.println(book);
}
}
5-5、sql片段
定义sql片段
<!--sql片段 -->
<sql id="select_by_condition">
<if test="t.bookName!=null and t.bookName!=''">
and bookName like concat('%',concat(#{t.bookName},'%'))
</if>
<if test="t.typeNo!=null and t.typeNo!=0">
and typeNo=#{t.typeNo}
</if>
<if test="t.beginTime!=null">
and cpDate > #{t.beginTime}
</if>
<if test="t.endTime!=null">
and cpDate < #{t.endTime}
</if>
</sql>
引用sql片段
<!--多条件分页查询 -->
<select id="queryBookByPage" parameterType="PageBean" resultType="Book">
select * from (select b.*,rownum r from tbl_book b
<where>
<!--引用sql片段-->
<include refid="select_by_condition"></include>
</where>
)where r > #{beginRecords} and r <= #{endRecords}
</select>
6、高级映射(resultMap)
resultMap可以实现延迟加载
6-1、一对一(association)
<!--
resultMap映射配置
column不做限制,可以为任意表的字段,而property须为type 定义的pojo属性
-->
<resultMap type="com.upinthewind.pojo.Book" id="bookResultMap">
<id column="bookno" property="bookno"/>
<result column="bookname" property="bookname"/>
<result column="author" property="author"/>
<result column="cps" property="cps"/>
<result column="cpdate" property="cpdate"/>
<result column="kucun" property="kucun"/>
<!--
association 用与映射关联查询的单个对象信息
property 要将关联查询的对象信息映射到book对象的哪个属性
-->
<association property="booktype" javaType="com.upinthewind.pojo.BookType">
<id column="typeno" property="typeno"/>
<result column="typename" property="typename"/>
</association>
</resultMap>
<select id="queryAllBook" resultMap="bookResultMap">
SELECT bookno,bookname,b.typeno,author,cps,cpdate,kucun,typename
FROM tbl_book b INNER JOIN tbl_type t ON b.typeno=t.typeno
</select>
6-2、一对多(collection)
Role角色实体类
public class Role {
private Integer rid;
private List<Power> powers;
private String rname;
Power权限实体类
public class Power {
private Integer pid;
private String pcontent;
private String url;
RoleDaoMapper.xml
<mapper namespace="com.upinthewind.dao.RoleDaoMapper">
<!--自定义结果集-->
<resultMap type="role" id="roleAndPower">
<id column="rid" property="rid"/>
<result column="rname" property="rname"/>
<!--role中包含power的集合-->
<collection property="powers" column="rid" ofType="power"
select="com.upinthewind.dao.PowerDaoMapper.queryPowerById">
</collection>
</resultMap>
<select id="queryRoleById" parameterType="int" resultMap="roleAndPower">
select * from tbl_role where rid = #{rid}
</select>
</mapper>
PowerDaoMapper.xml
<mapper namespace="com.upinthewind.dao.PowerDaoMapper">
<select id="queryPowerById" parameterType="int" resultType="Power">
select p.* from tbl_power p inner join role_power rp on p.pid = rp.pid
where rp.rid=#{id}
</select>
</mapper>
6-3、多对多(collection)
7、延迟加载
association和collection具备延迟加载的功能,当需要某个条件时才进行相关的查询–按需查询
多表关联查询时,先查一张表,在按需求查询其他表的数据,即是延迟加载。使用assocation中的select去加载statement的id,如果当前mapper.xml中没有该id需要在id前面加上其他mapper.xml的namespace
7-1、assocation
BookDaoMapper.xml
<mapper namespace="com.upinthewind.dao.BookDaoMapper">
<!--
resultMap映射配置
column不做限制,可以为任意表的字段,而property须为type 定义的pojo属性
-->
<resultMap type="com.upinthewind.pojo.Book" id="bookResultMap">
<id column="bookno" property="bookno"/>
<result column="bookname" property="bookname"/>
<result column="author" property="author"/>
<result column="cps" property="cps"/>
<result column="cpdate" property="cpdate"/>
<result column="kucun" property="kucun"/>
<!--
association 用与映射关联查询的单个对象信息
property 要将关联查询的对象信息映射到book对象的哪个属性
column 根据哪个字段进行关联查询(外键)
select 关联查询的mapper.xml的namespace.id
-->
<association property="booktype" javaType="BookType" column="typeno"
select="com.upinthewind.dao.BookTypeDaoMapper.queryTypeByNo">
</association>
</resultMap>
<select id="queryAllBook" resultMap="bookResultMap">
select * from tbl_book
</select>
</mapper>
BookTypeDaoMapper.xml
<mapper namespace="com.upinthewind.dao.BookTypeDaoMapper">
<select id="queryTypeByNo" parameterType="int" resultType="BookType">
select * from tbl_type where typeNo = #{typeNo}
</select>
</mapper>
7-2、collection
RoleDaoMapper.xml
<mapper namespace="com.upinthewind.dao.RoleDaoMapper">
<!--自定义结果集-->
<resultMap type="role" id="roleAndPower">
<id column="rid" property="rid"/>
<result column="rname" property="rname"/>
<!--role中包含power的集合-->
<collection property="powers" column="rid" ofType="power"
select="com.upinthewind.dao.PowerDaoMapper.queryPowerById">
</collection>
</resultMap>
<select id="queryRoleById" parameterType="int" resultMap="roleAndPower">
select * from tbl_role where rid = #{rid}
</select>
</mapper>
PowerDaoMapper.xml
<mapper namespace="com.upinthewind.dao.PowerDaoMapper">
<select id="queryPowerById" parameterType="int" resultType="Power">
select p.* from tbl_power p inner join role_power rp on p.pid = rp.pid
where rp.rid=#{id}
</select>
</mapper>
8、查询缓存
如果缓存中有数据,则不需要去数据库进行获取,用于减轻数据库的压力,提高数据库的性能
8-1、一级缓存(sqlSession)
1、sqlSession对象有一个HashMap数据结构,用于缓存数据,不同sqlSession对象的HashMap之间互不影响。
3、如果sqlSession执行了commit方法,则会清空一级缓存,是为了保证缓存区内的信息是最新的,避免出现脏读。
4、正常开发,是将mybatis与spring整合开发,事务控制在service层,一个service方法可以调用多个mapper。service方法执行时,开启事务,创建sqlSession对象,结束时关闭sqlSession。
8-2、二级缓存(Mapper)
1、一级缓存在方法结束时,会关闭sqlSession,必然会清空一级缓存
2、多个sqlSession去操作同一个Mapper的sql语句,多个sqlSession操作数据库得到的数据会存在二级缓存区域,二级缓存是跨sqlSession的,是多个sqlSession共用的。
3、二级缓存与一级缓存区别:二级缓存区(namespace)更大,是所有访问该Mapper的sqlSession共享
4、执行commit,清空二级缓存
使用
<setting name="cacheEnabled" value="true"/>
2、在Mapper.xml中开启二级缓存
<!--
type : 指定cache接口的实现类型,mybatis默认使用PerpetualCache
-->
<cache type=""/>
3、该Mapper对应的实体类要实现序列化(为了将缓存数据取出,反序列化)
implements Serializable
8-3、分布式缓存
对缓存数据进行集中管理,使用分布式缓存框架(redis、memcached、ehcache)。
mybatis无法实现分布式管理,需要与其它分布式缓存框架整合使用。
实现cache接口
9、注解方式
9-1、单表查询
public interface UserDaoMapper {
User queryUser(User user);
@Insert("INSERT INTO tbl_user VALUES(seq_user.nextval, #{uname}, #{pwd}, #{role.rid})")
int addUser(User user);
@Delete("delete from tbl_user where uno = #{uno}")
int deleteUser(Integer uno);
}
9-2、一对一
public interface IPersonDao {
@Select("select * from person where pid = #{pid}")
@Results({
@Result(id=true,column="pid",property="pid"),
@Result(column="pname",property="pname"),
@Result(column="page",property="page"),
@Result(column="pid",property="card",one=@One
(select="com.desert.dao.ICardDao.getCard",fetchType= FetchType.EAGER)) })
public Person getPerson(int pid);
}
9-3、一对多
public interface RelationMapper {
@Select("select id,name from TEACHER")
@Results({
@Result(id="true" property = "id",column = "id"),
@Result(property = "name",column = "name"),
@Result(property = "students",javaType = List.class,column ="id",
many = @Many(select = "com.example.mapper.RelationMapper.findStudents"))})
List<TeacherVo> findTeacherAndStudents();
@Select("select s.name from RELATION r,STUDENT s where s.id = r.sid and r.tid = #{tid}")
List<Student> findStudents(Long tid);
}
10、数据库数据模型分析
1、每张表记录的数据内容
2、每张表的重要字段设置
非空字段、外键关联字段
外键关系
4、表与表之间的业务关系
分析表与表之间的业务关系时,要建立在某个业务意义基础上进行分析
逆向工程
generatorConfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="testTables" targetRuntime="MyBatis3">
<commentGenerator>
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!-- 连接数据库 -->
<jdbcConnection driverClass="com.MysqL.jdbc.Driver"
connectionURL="jdbc:MysqL://localhost:3306/mybatis" userId="root" password="">
</jdbcConnection>
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- targetPackage:生成po类的包名 targetProject:生成po类的位置 -->
<javaModelGenerator targetPackage="com.mybatis.po"
targetProject="./src">
<property name="enableSubPackages" value="true" />
<property name="trimstrings" value="true" />
</javaModelGenerator>
<!-- targetPackage:mapper接口文件生成的包名 targetProject:mapper接口文件生成的项目位置-->
<sqlMapGenerator targetPackage="com.mybatis.dao"
targetProject="./src">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!-- targetPackage:mapper映射文件生成的包名 targetProject:mapper映射文件生成的项目位置-->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.mybatis.dao" targetProject="./src">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!-- 配置需要生成的表 -->
<table tableName="user" domainObjectName="user"></table>
<table tableName="orders" domainObjectName="orders"></table>
<table tableName="orderdetail" domainObjectName="orderdetail"></table>
<table tableName="items" domainObjectName="items"></table>
</context>
</generatorConfiguration>
GeneratorsqlMap.java
public class GeneratorsqlMap {
public void generator() throws Exception{
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
//指定逆向工程配置文件
File configFile = new File("generatorConfig.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
myBatisGenerator.generate(null);
}
public static void main(String[] args) {
GeneratorsqlMap generatorsqlMap = new GeneratorsqlMap();
try {
generatorsqlMap.generator();
} catch (Exception e) {
e.printstacktrace();
}
}
}