文章目录
MyBatis
基于Java的数据持久层(ORM)框架。把实体类和SQL语句之间建立了映射关系,是一种半自动化的ORM实现。
(1)ORM对象关系映射,一种数据持久化技术。
O:Object 对象===java中的实体类---对象
R:Relation 关系===关系型数据库---表
M:Mapping 映射===提供一种映射的机制
(2)半自动化:在映射过程中需要写一些SQL语句才能完成指定功能
搭建
-
创建Maven工程。
-
添加项目依赖。
<!--MyBatis项目依赖包-->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!-- Mysql驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<!-- junit测试包 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- 日志包 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
- 创建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>
<!--环境-->
<environments default="development">
<environment id="development">
<!--事务管理-->
<transactionManager type="JDBC"/>
<!--数据源设置-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/ssm_db"/>
<property name="username" value="root"/>
<property name="password" value="rootstart"/>
</dataSource>
</environment>
</environments>
<!--加载mapper映射文件-->
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.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.cwd.mybatisdemo.userDao">
<select id="selectBlog" resultType="com.cwd.mybatisdemo.User">
select *
from Blog
where id = #{id}
</select>
</mapper>
- 测试。
核心配置文件
<!ELEMENT configuration (properties?, settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, reflectorFactory?, plugins?, environments?, databaseIdProvider?, mappers?)>
核心配置文件sqlMapperConfig中的标签顺序应按照上面👆的顺序:properties—>mappers
数据库环境配置
<!--环境-->
<environments default="development">
<environment id="development">
<!--事务管理-->
<transactionManager type="JDBC"/>
<!--数据源设置-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/ssm_db"/>
<property name="username" value="root"/>
<property name="password" value="rootstart"/>
</dataSource>
</environment>
</environments>
属性设置
1.在属性中配置对应的数据。
<!--属性引用-->
<properties>
<property name="driver" value="com.mysql.jdbc.Driver"/>
</properties>
<!--环境-->
<environments default="development">
<environment id="development">
<!--事务管理-->
<transactionManager type="JDBC"/>
<!--数据源设置-->
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/ssm_db"/>
<property name="username" value="root"/>
<property name="password" value="rootstart"/>
</dataSource>
</environment>
</environments>
2.提取属性到一个全新的文件(jdbc.properties)进行引用
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/ssm_db
username=root
password=rootstart
<configuration>
<!--属性引用-->
<properties resource="jdbc.properties">
</properties>
<!--环境-->
<environments default="development">
<environment id="development">
<!--事务管理-->
<transactionManager type="JDBC"/>
<!--数据源设置-->
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
</configuration>
3.赋默认值(以password为例)
<configuration>
<!--属性引用-->
<properties resource="jdbc.properties">
<!--开启默认值-->
<property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/>
</properties>
<!--环境-->
<environments default="development">
<environment id="development">
<!--事务管理-->
<transactionManager type="JDBC"/>
<!--数据源设置-->
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password:rootstart}"/>
</dataSource>
</environment>
</environments>
</configuration>
设置setting
<!--设置-->
<settings>
<!--开启log4j日志-->
<setting name="logImpl" value="LOG4J"/>
<!--开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
<!--开启延迟加载/懒加载-->
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
类型别名
1.标签设置,依次设置
<typeAliases>
<typeAlias type="com.cwd.mybatisdemo.User" alias="user"/>
</typeAliases>
2.直接设置一个包
<!--类型别名-->
<typeAliases>
<package name="com.cwd.mybatisdemo"/>
</typeAliases>
其它配置项
- 类型处理器 typeHandlers
- 对象工厂 objectFactory
- 插件 plugins
- 映射器 mappers
<!--加载mapper映射文件-->
<mappers>
<mapper resource="UserDao.xml"/>
</mappers>
逆向工程
Mybatis需要自己编写SQL,若是表太多的话难免会麻烦,所以官方提供了一种逆向工程,可以针对单个表自动生成Mybatis执行所需要的代码(包括Mapper.xml、mapper.java、pojo)。一搬我们在开发中常用的逆向工程是通过数据库的表生成代码。
创建逆向工程
- 新建一个maven工程。
- 添加依赖的jar包。
<!-- Mysql驱动包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<!-- 日志包 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--代码生成工具-->
<!-- https://mvnrepository.com/artifact/org.mybatis.generator/mybatis-generator-core -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.7</version>
</dependency>
- 添加配置文件generatorConfig.xml文件,用于设置数据库驱动,配置,包名,文件保存位置,表名等等。
- 定义GeneratorSqlmap类,调用MyBatis自动创建接口,在main方法执行自动创建。
使用为主!!!
使用逆向工程
- 添加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>
<!-- 是否去除自动生成的注释 true:是 : false:否 -->
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/ssm_db"
userId="root"
password="rootstart">
</jdbcConnection>
<!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL
和 NUMERIC 类型解析为java.math.BigDecimal -->
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- targetProject:生成PO类的位置
注意:mac电脑写的地址是./src
windows电脑写的是.\src
-->
<javaModelGenerator targetPackage="main.java.com.cwd.mybatisdemo"
targetProject="./src">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
<!-- 从数据库返回的值被清理前后的空格 -->
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- targetProject:mapper映射文件生成的位置
注意:mac电脑写的地址是./src
windows电脑写的是.\src
-->
<sqlMapGenerator targetPackage="main.resources.mapper"
targetProject="./src">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</sqlMapGenerator>
<!-- targetPackage:mapper接口生成的位置
注意:mac电脑写的地址是./src
windows电脑写的是.\src
-->
<javaClientGenerator type="XMLMAPPER"
targetPackage="main.java.com.cwd.mybatisdemo" targetProject="./src">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</javaClientGenerator>
<!-- 指定数据库表
tableName 表名
domainObjectName 生成实体类对应的名称
-->
<table tableName="t_role" domainObjectName="Role" enableCountByExample="false"
enableUpdateByExample="false" enableDeleteByExample="false"
enableSelectByExample="false" selectByPrimaryKeyQueryId="false"
>
<columnOverride column="id" javaType="Integer"/>
</table>
</context>
</generatorConfiguration>
- 运行逆向工程的服务程序
public class GeneratorSqlmap {
public void generator() throws Exception {
List<String> warnings = new ArrayList<>();
boolean overwrite = true;
//加载核心配置文件
File configFile = new File("/Users/caiweidong/IdeaProjects/myCode/mybatisDemo/src/main/resources/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);
}
//程序入口^o^
public static void main(String[] args) throws Exception {
GeneratorSqlmap generatorSqlmap = new GeneratorSqlmap();
generatorSqlmap.generator();
}
}
动态SQL
动态SQL可以解决JDBC或其它类似框架根据不同条件拼接SQL语句时需要注意各种规则的问题,例如拼接时需要注意空格、去掉列表最后一个列名逗号等等这些问题。
MyBatis提供了各种标签对条件作出判断可以实现动态拼接SQL语句。
- if标签(实现查询)
if进行判断,test的内容是表达式,使用like进行模糊查询,%作为占位符, 进 行 拼 接 查 询 , c o n c a t ( ) 表 示 字 符 串 拼 接 , {}进行拼接查询,concat()表示字符串拼接, 进行拼接查询,concat()表示字符串拼接,{}无法被识别为字符串,因此要加单引号,#{}可以被识别为字符串,无需加单引号。由于在mapper文件中<会被识别成一个标签的开始,因此我们的< <= 是没有办法直接写的,因此需要转译:<![CDATA[ < ]]>
select * from t_role where id > #{id}
<if test="name != null">
and name like "%${name}%"
</if>
select * from t_role where id>#{id}
<if test="name != null">
and name like concat('%','${name}','%')
</if>
select * from t_role where id<![CDATA[<]]>#{id}
<if test="name != null">
and name like concat('%',#{name},'%')
</if>
- if-where标签(实现查询)
当我们无法判断是否有条件时,使用where标签框架会自动判断我们是否需要where,如果所有条件都不满足就会查询出所有的记录,并且可以自动消除第一个and和or。
select * from t_role
<where>
<if test="id != null">
and id<![CDATA[<]]>#{id}
</if>
<if test="name != null">
and name like concat('%',#{name},'%')
</if>
</where>
- choose-when-otherwise标签(实现查询)
当我们不想使用所有的条件,只是想从多个条件中选择一个使用时,我们可以选择choose标签,类似于switch语句。choose标签可以按照顺序判断when标签中的条件是否成立,有一个成立时choose就会结束。当when的所有条件都不满足时,会执行otherwise中的语句。类比switch语句,chooseswitch,whencase,otherwise==default。
select * from t_role where
<choose>
<when test="id != null">
id<![CDATA[<]]>#{id}
</when>
<when test="name != null">
and name like concat('%',#{name},'%')
</when>
<otherwise>
userid = #{userid}
</otherwise>
</choose>
- trim-if标签(实现添加)
trim标签可以在在自己包含的内容前加上某些前缀,也可以在其后加上某些后缀,与之对应的属性是prefix和suffix。或将包含内容的首部(或尾部)某些内容覆盖,对应的属性是prefixOverrides和suffixOverrides。
/*insert into 表名(列名,列名,列名) values(值,值,值)*/
insert into t_role
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="name != null">
name,
</if>
<if test="userid != null">
userid,
</if>
<if test="operTime != null">
oper_time,
</if>
</trim>
values
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="name != null">
#{name},
</if>
<if test="userid != null">
#{userid},
</if>
<if test="operTime != null">
#{operTime},
</if>
</trim>
- set-if-where标签(实现修改)
set标签用在修改的操作,作用和where差不多,使用set标签可以在包含的语句前面输出一个set,若包含的语句以逗号结尾会将逗号省略
<update id="updateRole" parameterType="role">
/*update t_role set 列1=值1,列2=值2 where 列1=值1 and 列2=值2*/
update t_role
<set>
<if test="name != null">
name = #{name},
</if>
<if test="userid != null">
userid = #{userid},
</if>
<if test="operTime != null">
oper_time = #{operTime},
</if>
</set>
<where>
<if test="id != null">
and id = #{id}
</if>
<if test="id == null">
and id = -1
</if>
</where>
</update>
-
foreach标签-构建in条件
foreach标签用于构建in条件,在SQL语句中进行迭代一个集合。
foreach标签的属性主要有:
属性 | 作用 |
---|---|
item | 表示迭代集合中每一个元素的别名。 |
index | 指定一个名字表示在迭代过程中每次迭代到的位置。 |
open | 表示语句以什么开始。 |
separator | 表示每次迭代以什么符号作为分割符。 |
close | 表示语句以什么为结束。 |
collection | 单参且参数类型为List,属性为list;单参且类型为数组,属性为array;多参为Map。 |
实际如果在传入参数的时候,在MyBatis里面也会把他封装成一个Map的,Map的key就是参数名,所以在这个时候collection属性值就是传入的List或Array对象在自己封装的Map里面的Key。
<!--第一种,直接使用foreach标签-->
<select id="selectRoleById" parameterType="java.util.List" resultType="com.cwd.mybatisSQL.bean.Role">
select * from t_role
<where>
<foreach collection="list" open="(" close=")" item="roleId" separator="or">
id = #{roleId}
</foreach>
</where>
</select>
<!--第二种,通过foreach标签构建in-->
<select id="selectRoleById" parameterType="java.util.List" resultType="com.cwd.mybatisSQL.bean.Role">
select * from t_role where id in
<foreach collection="list" open="(" close=")" item="roleId" separator=",">
#{roleId}
</foreach>
</select>
<!--第三种,集合类型为array-->
<select id="selectRoleById" parameterType="java.util.List" resultType="com.cwd.mybatisSQL.bean.Role">
select * from t_role where id in
<where>
<foreach collection="array" open="(" close=")" item="roleId" separator=",">
id = #{roleId}
</foreach>
</where>
</select>
<!--第四种,参数转换为Map集合,map的key为roleIds,一般有多条复杂查询时使用Map集合-->
<select id="selectRoleByMap" parameterType="java.util.Map" resultType="com.cwd.mybatisSQL.bean.Role">
select * from t_role where id in
<foreach collection="roleIds" open="(" close=")" item="roleId" separator=",">
#{roleId}
</foreach>
</select>
- SQL片段(重复利用相同的SQL片段)
<sql id = "sqlid">
select * from t_role
</sql>
<select id="selectRoleById" parameterType="java.util.List" resultType="com.cwd.mybatisSQL.bean.Role">
<include refid="sqlid"></include>
where id in
<foreach collection="list" open="(" close=")" item="roleId" separator="or">
#{roleId}
</foreach>
</select>
<select id="selectRoleByMap" parameterType="java.util.Map" resultType="com.cwd.mybatisSQL.bean.Role">
<include refid="sqlid"></include>
where id in
<foreach collection="roleIds" open="(" close=")" item="roleId" separator=",">
#{roleId}
</foreach>
</select>
-
bind标签
允许在OGNL表达式外创建一个变量,并将其绑定在当前的上下文。
<!--以模糊查询为例-->
<select id="selectRoleByIdName" parameterType="role" resultType="role">
<bind name="name" value=" '%'+name+'%' "/>
select * from t_role
<where>
<if test="id != null">
and id<![CDATA[<]]>#{id}
</if>
<if test="name != null">
and name like #{name}
</if>
</where>
</select>
MyBatis查询
MyBatis进行select映射时,返回类型可以用resultType,也可以使用resultMap。resultType是直接表示返回类型的(即我们的model对象中的实体);而resultMap则是对外部ResultMap的引用(即提前定义了数据库和实体类之间的映射key-value关系),两者不能同时存在。
在MyBatis中进行查询映射时,查询的每一个属性都是放在一个对应的Map中的,其中key是属性名,value是对应的值。(1)返回类型是resultType时,MyBatis会将Map里面的键值对取出来赋给resultType所指定的对象对应的属性。所以其实MyBatis的每一个查询映射的返回类型都是ResultMap,只是当提供的返回类型属性时resultType时,MyBatis会自动的把对应的质感赋给resultType所指定对象的属性;(2)当返回值类型是resultMap时,因为Map不能很好的表示领域模型,就需要自己再进一步的把它转化为对应的对象,这常常在复杂查询中很有作用。
一对一关联查询
查询所有员工信息及员工对应的部门信息。
-
使用resultType属性。
(1)创建包含员工和部门信息的一个实体类。
(2)创建实体类对应的mapper接口。
(3)创建mapper.xml映射文件。
(4)编写测试类。
(5)存在问题:多创建了一组实体类及其对应的mapper文件。
-
使用resultMap属性。
(1)分别创建员工和部门信息的实体类,在其中一个类中添加另一个类作为属性。
public class User {
private Integer id;
private String account;
private String password;
private Integer type;
private String fileName;
private Role role;
}
public class Role {
private Integer id;
private String name;
private Integer userid;
private Date operTime;
}
(2)创建对应的mapper接口。
(3)创建对应的Mapper.xml文件。
<resultMap id="userAndRole" type="user">
<id column="u.id" property="id"/>
<result column="u.account" property="account"/>
<result column="u.password" property="password"/>
<result column="u.type" property="type"/>
<result column="u.file_name" property="fileName"/>
<association property="role" javaType="role">
<id column="r.id" property="id"/>
<result column="r.name" property="name"/>
<result column="r.userid" property="userid"/>
<result column="r.oper_time" property="operTime"/>
</association>
</resultMap>
<select id="selectAllUsers" resultMap="userAndRole">
sql语句
</select>
一对多关联查询
使用resultMap。
public class User {
private Integer id;
private String account;
private String password;
private Integer type;
private String fileName;
private List<Role> roles;
}
public class Role {
private Integer id;
private String name;
private Integer userid;
private Date operTime;
}
<!--
1.collection标签用于封装查询到的多条数据信息。
2.collection标签中的property属性是java类中集合属性所对应的对象名。
3.collection标签中的javaType属性是所用集合的类型,如list或者set等。
4.collection标签中的ofType属性是集合封装的对象类型。
-->
<resultMap id="userAndRole" type="com.cwd.mybatisSelect.bean.User">
<id column="uid" property="id"></id>
<result column="uaccount" property="account"></result>
<result column="upassword" property="password"></result>
<result column="utype" property="type"></result>
<result column="ufilename" property="fileName"></result>
<collection property="roles" ofType="com.cwd.mybatisSelect.bean.Role" javaType="list">
<id column="rid" property="id"></id>
<result column="rname" property="name"></result>
<result column="ruserid" property="userid"></result>
<result column="ropertime" property="operTime"></result>
</collection>
</resultMap>
<select id="selectUserAndRole" resultMap="userAndRole" parameterType="integer">
SELECT
u.id as uid,
u.account as uaccount,
u.PASSWORD as upassword,
u.type as utype,
u.file_name as ufilename,
r.id as rid,
r.NAME as rname,
r.userid as ruserid,
r.oper_time as ropertime
FROM
t_user AS u
INNER JOIN user_role AS ur ON u.id = ur.userid
INNER JOIN t_role AS r ON ur.roleid = r.id
WHERE
u.id = #{userId}
</select>
多对多关联查询
准备表
--用户表
CREATE table users(
uid int(4) not null,
uname VARCHAR(20) DEFAULT null,
sex VARCHAR(3) DEFAULT null,
birthday date default null,
address VARCHAR(100) default null,
PRIMARY Key(uid)
)
--订单表
create table orders(
oid int(4) not null,
userid int(4) default null,
orderid VARCHAR(20) DEFAULT null,
createtime date default null,
status varchar(10) default null,
primary key(oid)
)
--订单详情表
create table orderdetail(
odid int(4) not null PRIMARY key,
orderid VARCHAR(20) DEFAULT null,
itemid int(4) DEFAULT null,
itemnum int(4) DEFAULT null
)
--商品表
CREATE TABLE items(
iid int(4) not null,
name VARCHAR(30) DEFAULT NULL,
detail VARCHAR(50) DEFAULT NULL,
price DOUBLE(5,2) DEFAULT NULL,
PRIMARY KEY(iid)
)
问题:获得所有用户的所有订单里的所有商品信息!
(1)创建四张表对应的四个实体类。
(2)创建对应的mapper接口。
(3)创建对应的mapper.xml文件。
<!--resultMap嵌套-->
<resultMap id="infoAll" type="users">
<id column="uid" property="uid"/>
<result column="uname" property="uname"/>
<result column="sex" property="sex"/>
<result column="birthday" property="birthday"/>
<result column="address" property="address"/>
<collection property="orders" ofType="orders" javaType="list">
<id column="oid" property="oid"/>
<result column="orderid" property="orderid"/>
<result column="createtime" property="createtime"/>
<result column="status" property="status"/>
<collection property="ods" ofType="orderDetail" javaType="list">
<id column="odid" property="odid"/>
<result column="itemnum" property="itemnum"/>
<association property="items" javaType="items">
<id column="iid" property="iid"/>
<result column="name" property="name"/>
<result column="detail" property="detail"/>
<result column="price" property="price"/>
</association>
</collection>
</collection>
</resultMap>
<select id="selectInfoById" parameterType="int" resultMap="infoAll">
SELECT
u.uid,
u.uname,
u.sex,
u.birthday,
u.address,
o.oid,
o.orderid,
o.createtime,
o.STATUS,
od.odid,
od.itemnum,
i.iid,
i.NAME,
i.detail,
i.price
FROM
users AS u
INNER JOIN orders AS o ON u.uid = o.userid
INNER JOIN orderdetail AS od ON o.orderid = od.orderid
INNER JOIN items AS i ON od.itemid = i.iid
WHERE
u.uid = #{uid}
</select>
分页查询
方案一:使用Page工具类
- 创建util工具类page
public class Page<T> {
//获得表里一共有多少条数据
private Integer dataCount;
//每页显示多少条数据
private Integer showData;
//一共分成多少页
private Integer pageCount;
//当前是第几页
private Integer pageIndex;
//当前页面显示的集合信息
private List<T> list;
public Integer getDataCount() {
return dataCount;
}
public void setDataCount(Integer dataCount) {
this.dataCount = dataCount;
}
public Integer getShowData() {
return showData;
}
public void setShowData(Integer showData) {
this.showData = showData;
}
public Integer getPageCount() {
//数据库的总条数如果能除尽每页显示的条数就直接赋值,若不能则除数取整+1
return this.pageCount = this.dataCount%this.showData==0 ? this.dataCount/this.showData:this.dataCount/this.showData+1;
}
public Integer getPageIndex() {
return pageIndex;
}
public void setPageIndex(Integer pageIndex) {
this.pageIndex = pageIndex;
}
public List<T> getList() {
return list;
}
public void setList(List<T> list) {
this.list = list;
}
}
- 创建接口方法,获取数据库总条数和第x列的数据集合
//分页获得一共有多少条数据
int selectCount() throws Exception;
//获得当前页显示的集合信息
List<Users> selectUsersByPage(Page<Users> page) throws Exception;
- 创建两个方法对应的xml文件
<!--用于分页,一共有多少条数据-->
<select id="selectCount" resultType="java.lang.Integer">
select count(uid) from users;
</select>
<!--用于分页,获得当前页显示的集合信息-->
<select id="selectUsersByPage" parameterType="com.cwd.mybatisSelect.util.Page" resultType="com.cwd.mybatisSelect.bean.Users">
<bind name="beginIndex" value="(pageIndex-1)*showData+1"/>
select * from users order by uid limit #{beginIndex},#{showData}
</select>
- 调用方法进行使用
public class test {
private SqlSessionFactory factory;
@Before
public void init() throws IOException {
InputStream res = Resources.getResourceAsStream("sqlMapperConfig.xml");
SqlSessionFactoryBuilder fac = new SqlSessionFactoryBuilder();
factory = fac.build(res);
}
@Test
public void test2() throws Exception {
SqlSession sqlSession = factory.openSession();
UsersMapper mapper = sqlSession.getMapper(UsersMapper.class);
Page<Users> page = new Page<>();
//一共有多少条数据
int dataCount = mapper.selectCount();
System.out.println(dataCount);
page.setDataCount(dataCount);
page.setShowData(2);
page.setPageIndex(1);
//获取对应页的数据集合
List<Users> users = mapper.selectUsersByPage(page);
System.out.println(users);
page.setList(users);
sqlSession.close();
for (Users user : page.getList()) {
System.out.println(users);
}
}
}
方案二:使用RowBounds插件
- 创建实体类.
- 创建接口,参数使用RowBounds
//RowBounds插件,分页插件
List<Items> selectItems(RowBounds rb) throws Exception;
- 创建对应的xml文件
<select id="selectItems" resultType="com.cwd.mybatisSelect.bean.Items">
select * from items;
</select>
- 测试使用
@Test
public void test3() throws Exception {
SqlSession sqlSession = factory.openSession();
UsersMapper mapper = sqlSession.getMapper(UsersMapper.class);
//new RowBounds(从第几个下标开始查,查多少条数据);
RowBounds rb = new RowBounds(3,2);
List<Items> items = mapper.selectItems(rb);
sqlSession.close();
System.out.println(items);
}
延迟加载(Lazy Load)
在数据与对象进行mapping映射操作是,只有在真正使用到该对象时,才进行mapping操作,这样可以减少数据库查询的开销,提升系统的性能。但是Lazy Load也是有缺点的,在按需加载时会多次连接数据库,会增加数据库的压力,所以我们需要在现实中斟酌使用。resultMap可以实现高级映射,association、collection都具备延迟加载的功能。
延迟加载配置
- myBatis默认没有开启延迟加载,需要在MyBatis的核心配置文件中的setting进行配置。
<settings>
<!--开启延迟加载/懒加载 默认为false-->
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
- aggressiveLazyLoading:value为true,延迟加载开启时,访问一个对象中的懒对象属性时,将完全加载这个对象的所有懒对象属性。value为false,延迟加载开启时,按需加载对象属性。
<settings>
<!--开启延迟加载/懒加载-->
<setting name="lazyLoadingEnabled" value="true"/>
<!--将积极加载改为消极加载 既按需加载-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
实现延迟加载
- 准备实体类OrserDetail(订单详情)和Items(商品信息)。
public class OrderDetail {
private Integer odid;
private String orderid;
//private Integer itemid;
private Integer itemnum;
private Items items;
}
public class Items {
private Integer iid;
private String name;
private String detail;
private Double price;
}
- 准备两个mapper接口
public interface OrderDetailMapper {
OrderDetail selectOrserDatailLazyLoad(int odid) throws Exception;
}
public interface ItemsMapper {
Items selectItemsById(int id) throws Exception;
}
- 准备两个mapper映射文件
<!--OderDeatilmapper.xml文件-->
<resultMap id="OrderDetailItemLazyLoad" type="orderDetail">
<id column="odid" property="odid"/>
<result column="orderid" property="orderid"/>
<result column="itemnum" property="itemnum"/>
<association property="items" javaType="items"
select="com.cwd.mybatisSelect.dao.ItemsMapper.selectItemsById"
column="itemid"/>
</resultMap>
<select id="selectOrserDatailLazyLoad" resultMap="OrderDetailItemLazyLoad">
select * from orderdetail where odid = #{odid}
</select>
<!--ItemsMapper.xml文件-->
<select id="selectItemsById" parameterType="int" resultType="items">
select * from items where iid = #{iid}
</select>
- 测试代码
@Test
public void test4() throws Exception {
SqlSession sqlSession = factory.openSession();
OrderDetailMapper orderDetailMapper = sqlSession.getMapper(OrderDetailMapper.class);
ItemsMapper itemsMapper = sqlSession.getMapper(ItemsMapper.class);
OrderDetail orderDetail = orderDetailMapper.selectOrserDatailLazyLoad(2);
sqlSession.close();
System.out.println(orderDetail.getOrderid());
System.out.println(orderDetail.getItems());
}
MyBatis查询缓存
缓存是用“空间换时间”的设计理念,利用内存空间的资源来提高检索速度的手段之一。MyBatis包含一个非常强大的查询缓存特性,可以方便的进行配置和定制,它提供了一级缓存和二级缓存。一级缓存基于PerpetualCache的HashMap本地缓存,存储的作用域是Session,当Session被flush或close后,该Session中的所有Cache就将被清空;
二级缓存与一级缓存的机制类似,默认使用PerpetualCahce(长期缓存),hashMap存储,但它的作用域为Mapper,可以自定义存储源(Ehcache、hazelcast等)。
一级缓存
MyBatis默认开启一级缓存,不需要进行配置,同一个SqlSession对象中,第二次进行相同的查询时,直接获取到缓存数据,不需要去执行sql再到数据库中纪念性查询。
同一个session:
获得session对象(开始)
--->
session.commit() 或 session.close() 或 session.flush()
--->
结束
二级缓存
MyBatis的二级缓存是Mapper范围级别,需要在MyBatis的全局配置文件中设置二级缓存的开关,还需要在具体的mapper.xml中开启二级缓存。
- 添加二级缓存的相关jar包依赖
<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.0.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/net.sf.ehcache/ehcache -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.10.1</version>
</dependency>
- 在Mybatis全局配置文件中手动开启配置文件
<settings>
<!--开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>
- 在各自的sql映射文件中单独开启二级缓存
<cache></cache>
- 实体类进行序列化
public class Items implements Serializable {
5.测试
@Test
public void test5() throws Exception {
SqlSession sqlSession = factory.openSession();
ItemsMapper itemsMapper = sqlSession.getMapper(ItemsMapper.class);
Items items = itemsMapper.selectItemsById(1);
System.out.println("第一个SqlSession的商品信息:"+items);
sqlSession.close();
SqlSession sqlSession1 = factory.openSession();
ItemsMapper itemsMapper1 = sqlSession1.getMapper(ItemsMapper.class);
Items items1 = itemsMapper1.selectItemsById(1);
System.out.println("第二个SqlSession的商品信息:"+items1);
sqlSession1.close();
}
二级缓存补充
-
禁用二级缓存
可以在sql语句所在标签使用属性useCache = “false” 用来禁用二级缓存
<select id="selectItemsById" parameterType="int" resultType="items" useCache="false">
- 刷新缓存
在同一个mapper作用域中, 如果有其他的DML(增删改)SQL语句时, 需要刷新缓存数据, 避免脏读, 默认情况下flushCache=“true” 会刷新缓存: 改为false则不会刷新
<update id="updateInfoByid" parameterType="int" flushCache="false">
执行SQl后不刷新缓存
</update>