【JAVA企业级开发】开源持久层ORM(对象关系型映射)框架:Mybatis(完整精细版)
一、Mybatis简介1.开源项目:开放源代码的项目2.Mybatis是一个开源的持久层(操控库表技术与jdbc是同样功能)框架。3.历史MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。iBATIS一词来源...
这里写目录标题
一级目录
二级目录
三级目录
一、Mybatis简介
1.开源项目:开放源代码的项目
2.Mybatis是一个开源的持久层(操控库表技术与jdbc是同样功能)框架。
3.历史
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
iBATIS一词来源于“internet”和“abatis”的组合
总的来说:
Mybatis.是一个基于Java的,在jdbc的基础之上封装而成的持久层框架。
MyBatis采用功能强大的基于OGNL(对象图形导航语言)的表达式来实现动态sql语句
下载地址:https://github.com/mybatis/mybatis-3
二,搭建mybatis环境
intellij idea设置默认工作目录:

1加载maven依赖和创建mysql表
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
创建一个表,有六个字段:id,account,password,brithday,mail,sex
CREATE TABLE EnUser (id int(11) NOT NULL,account bigint(12) NOT NULL DEFAULT ‘0’,password varchar(20) NOT NULL DEFAULT ‘’,brithday varchar(25) DEFAULT NULL,mail varchar(20) DEFAULT NULL,sex int(1) NOT NULL DEFAULT ‘0’,
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
int最多十一位,所以要用bigint
2 属性文件,配置文集,映射文件
resources下创建属性文件dbConfig.properties:
jdbc.url=jdbc:mysql://服务器外网ip:3306/mybatis
jdbc.driver=com.mysql.jdbc.Driver
jdbc.name=root
jdbc.password=password
resources下创建配置文件mybatisConfig.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 resource="dbconfig.properties"></properties> <!-- 配置文件中加载属性文件 -->
<environments default="baobaobaobao">
<!-- 配置一个数据库连接环境 -->
<environment id="baobaobaobao">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED"> <!--使用连接池的数据源,默认不使用连接池-->
<!-- url -->
<property name="url" value="${jdbc.url}"></property>
<!-- name -->
<property name="username" value="${jdbc.name}"></property>
<!-- password -->
<property name="password" value="${jdbc.password}"></property>
<!-- driver class -->
<property name="driver" value="${jdbc.driver}"></property>
</dataSource>
</environment>
</environments>
<!-- load mapping file -->
<mappers>
<mapper resource="mapper/EnUser.xml"></mapper>
</mappers>
</configuration>
resources/mapper下创建映射文件EnUser.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="baobaobaobao.EnUser">
<!-- insert -->
<insert id="insertUser">
insert into EnUser values(1,1440688330,'shuang666','1998-03-14','1440688330@qq.com',1)
</insert>
</mapper>
测试插入一条数据
三构造实体类,使用mybitsAPI连接操作mysql
package baobaobaobao;
import java.sql.Date;
/**
* @author
* Created by LiuChunhang on 2020/3/2.
*/
public class EnUser {
private int id;
@Override
public String toString() {
return "EnUser{" +
"id=" + id +
", account=" + account +
", password='" + password + '\'' +
", brithday=" + brithday +
", mail='" + mail + '\'' +
", sex=" + sex +
'}';
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
private int account;
private String password;
private Date brithday;
private String mail;
private int sex;
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Date getBrithday() {
return brithday;
}
public void setBrithday(Date brithday) {
this.brithday = brithday;
}
public String getMail() {
return mail;
}
public void setMail(String mail) {
this.mail = mail;
}
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
public int getAccount() {
return account;
}
public void setAccount(int account) {
this.account = account;
}
}
mybitsAPI:
package baobaobaobao;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.Reader;
/**
* Created by @author LiuChunhang on 2020/3/2.
*/
public class Testmybits {
public static void main(String[] args) throws IOException {
// 加载配置文件
Reader reader = Resources.getResourceAsReader("mybatisConfig.xml");
// 创建构造器对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
// 创建工厂
SqlSessionFactory sqlSessionFactory = builder.build(reader);
//创建执行对象
SqlSession session = sqlSessionFactory.openSession();
// 执行sql语句
session.insert("insertUser");
// 提交事务
session.commit();
session.close();
}}
运行代码,检查结果:
成功插入数据!
四 向SQL语句传参数方式
(1)传对象
<!-- update -->
<update id="updateUser" parameterType="baobaobaobao.EnUser">
update EnUser set
account=#{account},password=#{password},brithday=#{brithday},mail=#{mail},sex=#{sex} where
id=#{id}
</update>
@Test
public void testupdate() throws IOException {
//构造方法的名称必须和所在的类名称完全一样,就连大小写也要一样。
//构造方法不要写返回值类型,连void都不写。
//构造方法不能return一个具体的返回值。
//如果没有编写任何构造方法,那么编译器将默认会赠送一个构造方法,没有参数、方法体什么事情都不会做
//一旦编写了至少一个构造方法,那么编译器将不再赠送
EnUser user = new EnUser(1, 1440688330, "shuang666", "1998-03-14", "oxen@qq.com", 0);
// 加载配置文件
Reader reader = Resources.getResourceAsReader("mybatisConfig.xml");
// 创建构造器对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
// 创建工厂
SqlSessionFactory sqlSessionFactory = builder.build(reader);
//创建执行对象
SqlSession session = sqlSessionFactory.openSession();
// 执行sql语句
session.insert("updateUser",user);
// 提交事务
session.commit();
session.close();
}
(2)传map
<!-- query -->
<select id="queryMapUser" resultType="baobaobaobao.EnUser" parameterType="java.util.Map">
SELECT * from EnUser where account=#{account} and password=#{password}
</select>
@Test
public void TestMapSecelt() throws IOException {
HashMap hashMap = new HashMap<String,Object>();
hashMap.put("account",1440688330);
hashMap.put("password", "shuang666");
// 加载配置文件
Reader reader = Resources.getResourceAsReader("mybatisConfig.xml");
// 创建构造器对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
// 创建工厂
SqlSessionFactory sqlSessionFactory = builder.build(reader);
//创建执行对象
SqlSession session = sqlSessionFactory.openSession();
// 执行sql语句
List<EnUser> queryMapUser = session.selectList("queryMapUser", hashMap);
for (Object info:queryMapUser
) {
System.out.println(info); //会自动调用无参构造函数的tostring方法,所以必须要再加上一个无参构造函数,否则会报错No constructor found matching.
}
session.commit();
session.close();
}
}
四 基于接口实现crud
Mapper.xml文件中的namespace与mapper接口的类路径相同。
Mapper接口方法名和Mapper.xml中定义的每个statement的id相同。
Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的parameterType的类型相同。
Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的resultType的类型相同。
// 加载配置文件
Reader reader = Resources.getResourceAsReader("mybatisConfig.xml");
// 创建构造器对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
// 创建工厂
SqlSessionFactory sqlSessionFactory = builder.build(reader);
//创建执行对象
SqlSession session = sqlSessionFactory.openSession();
// 执行sql语句
List<EnUser> queryMapUser = session.selectList("query
MapUser", hashMap);
用接口实现更换为
userdao userdao = session.getmapper(userdao.class);
userdao.接口方法(参数);
session.commit();
session.close();
注:
1接口中方法的参数类型为 integer而不是int的原因是 integer可以为null !
可能在用接口泛型的时候出现以下语法错误:
①语法等级错误java1.5语法 不支持 diamond
Diamond types are not supported at language level '5'

解决:
5.0不支持6.0/7.0特性的代码,所以我把它设置为8,问题解决了。
②修改第一处错误以后可能会出现以下错误Error:java: Compilation failed: internal java compiler error
解决方法:进入编译器,修改版本和本机jdk版本保持一致即可。
五 动态sql与单例模式

1、常用标签
if、choose(when,otherwise)、trim(where,set)、foreach
2、多条件查询
choose, when, otherwise
select * from 表
Where 客户编号=? and 地区=?
Where 入库日期=? and 出库日期=? and 客户名称=?
Where 地区=?
Where 产品类别=?
Jdbc:
String sql=”select * from 表 where ”;
If(客户编号!=null){
sql+=”客户编号=?”;
}
If(地区!=null){
sql+=”and 地区=?”;
}
<!-- 动态sql语句 if -->
<select id="dynaIf" parameterType="DeptEntity" resultMap="deptMap">
SELECT <include refid="deptColumns"/> FROM dept where 1=1
<if test="dno>0">
and deptno=#{dno}
</if>
<if test="dname!=null">
and dname=#{dname}
</if>
<if test="dloc!=null">
and loc=#{dloc}
</if>
</select>
<select id="dynaChoose" parameterType="DeptEntity" resultMap="deptMap">
SELECT
<include refid="deptColumns" />
FROM dept where 1=1
<choose>
<when test="dno>0">
and deptno=#{dno}
</when>
<when test="dname!=null">
and dname=#{dname}
</when>
<otherwise>
and loc='天津'
</otherwise>
</choose>
</select>
3、where标签
<-- 动态sql where where标签如果与and、or的关键字相碰,and、or会被忽略 -->
<select id="dynaWhere" parameterType="DeptEntity" resultMap="deptMap">
SELECT
<include refid="deptColumns" />
FROM dept
<where>
<if test="dno>0">
deptno=#{dno}
</if>
<if test="dname!=null">
and dname=#{dname}
</if>
<if test="dloc!=null">
and loc=#{dloc}
</if>
</where>
4、trim标签
<–trim标签–>
<-- 动态sql trim trim标签提供一个where关键字 ,当where关键字与and|or相碰,and or会被忽略 -->
<select id="dynaTrim" parameterType="DeptEntity" resultMap="deptMap">
SELECT
<include refid="deptColumns" />
FROM dept
<trim prefix="where" prefixOverrides="and|or">
<if test="dno>0">
deptno=#{dno}
</if>
<if test="dname!=null">
and dname=#{dname}
</if>
<if test="dloc!=null">
and loc=#{dloc}
</if>
</trim>
</select>
5、set标签
用于更新转换set关键字
通常与if标签一起用,可用于保证更新空值
<update id="dynaUpdate" parameterType="DeptEntity">
update dept
<set>
<if test="dname!=null">
dname=#{dname},
</if>
<if test="dloc!=null">
loc=#{dloc}
</if>
</set>
where deptno=#{dno}
</update>
6、foreach标签
<-- 动态 sql foreach in item:定义变量,用于接收遍历出的集合元素,index:索引 collection:集合类型 open/close:迭代出的元素组合的开始/结束符号 separator:元素之间的分隔符 -->
批量查找
<select id="dynaForeachIn" parameterType="java.util.List"
resultMap="deptMap">
SELECT <include refid="deptColumns" /> FROM dept WHERE deptno IN
<foreach item="item" index="index" collection="list" open="(" separator="," close=")">
#{item}
</foreach>
</select>
<-- 批量插入 -->
<!- 动态sql batch insert -->
<insert id="forEachInsert" parameterType="java.util.List">
INSERT INTO dept(DEPTNO,DNAME,LOC) VALUES
<foreach item="item" index="index" collection="list" separator=",">
(#{item.dno},#{item.dname},#{item.dloc})
</foreach>
</insert>
<-- 批量删除 -->
<-- 动态sql batch delete -->
<delete id="forEachDelete" parameterType="java.util.List">
DELETE FROM dept WHERE deptno IN
<foreach item="item" index="index" collection="list" open="("
separator="," close=")">
#{item}(9,10,11)
</foreach>
</delete>
<-- 批量修改 -->
mybaits的批量修改以及其中的坑:需要修改url
修改url:
Mysql5.0:
url=jdbc:mysql://localhost:3306/数据库?allowMultiQueries=true
Mysql:8.0:
jdbc:mysql://localhost:3306/数据库?serverTimezone=Asia/Shanghai&allowMultiQueries=true
<update id="forEachUpdate" parameterType="java.util.List">
<foreach item="item" index="index" collection="list">
update dept
<set>
<if test="item.dname!=null">
dname=#{item.dname},
</if>
<if test="item.dloc!=null">
loc=#{item.dloc}
</if>
</set>
where deptno=#{item.dno};
</foreach>
</update>
注:
单例模式
SqlSessionFactory是线程安全的,最佳范围是与程序的生命周期一致。
SqlSession的实例不能被共享,也是线程不安全的。
/**
*
- 利用单例模式创建SqlSessionFactory对象
- 该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
*/
public class SqlSessionFactoryUtil {
//声明SqlSessionFactory 类型变量
private static SqlSessionFactory factory = null;
// 私有化构造方法
private SqlSessionFactoryUtil() {}
//返回一个SqlSessionFactory 对象
public static SqlSessionFactory getSqlSessionFactoryInstance() {
if (factory == null) {
// 加载配置文件
// 把配置文件读入字符输入流
Reader reader;
try {
reader = Resources.getResourceAsReader("mybatisConfig.xml");
// 构造器对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
// Session工厂对象
factory = builder.build(reader);
return factory;
} catch (IOException e) {
e.printStackTrace();
}
}
return factory;
}
}
六 mybits逆向工程
mybatis自动生成的代码只能操作单表,如果是处理多表的话,需要自己写sql
查看githup说明:
翻译:
这是生成器的主要版本。重要主题包括:
科特林!生成Kotlin代码的新运行时
更新了MyBatis动态SQL的运行时
取消对iBatis2的支持
更新到Java 8
还有许多其他增强功能和修复程序。请在此处阅读发行说明以获取详细信息:http : //mybatis.org/generator/whatsNew.html
您还可以在此处查看其他错误修复和增强功能的列表:https : //github.com/mybatis/generator/issues?q=milestone%3A1.4.0
新的工件可以在Maven Central中找到,网址为:
< 依存关系 >
< groupId > org.mybatis.generator </ groupId >
< artifactId > mybatis-generator </ artifactId >
< 版本 > 1.4.0 </ 版本 >
</ 依赖 >
和
< 依存关系 >
< groupId > org.mybatis.generator </ groupId >
< artifactId > mybatis-generator-maven-plugin </ artifactId >
< 版本 > 1.4.0 </ 版本 >
</ 依赖 >
您可以从此处的日食市场安装日食功能:https : //marketplace.eclipse.org/content/mybatis-generator
添加依赖:
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator</artifactId>
<version>1.4.0</version>
</dependency>
添加插件:
<build>
<plugins>
<!-- mybatis-generator反向工程 -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.4.0</version>
<configuration>
<!-- 在控制台打印执行日志 -->
<verbose>true</verbose>
<!-- 重复生成时会覆盖之前的文件-->
<overwrite>true</overwrite>
<configurationFile>src/main/resources/generatorConfig.xml</configurationFile>
</configuration>
</plugin>
</plugins>
</build>
或者在插件中直接指定 jdbc 驱动包,这样就不用在配置文件中写绝对路径<classPathEntry location="E:\MavenRepositoryForIdea\mysql\mysql-connector-java\5.1.6\mysql-connector-java-5.1.6.jar" /> 或者相对路径 location="classpath:"再次去指定了!
<build>
<plugins>
<!-- mybatis-generator反向工程 -->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.5</version>
<configuration>
<!-- 在控制台打印执行日志 -->
<verbose>true</verbose>
<!-- 重复生成时会覆盖之前的文件-->
<overwrite>true</overwrite>
<configurationFile>src/main/resources/generatorConfig.xml</configurationFile>
</configuration>
<!-- 此插件运行所要依赖的jar -->
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
添加generatorConfig.xml 内容如下
```powershell
<?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>
<!-- 数据库驱动,如果配置文件和上面两个jar在同一级目录,则可以直接如下填写,否则为了防止报错直接写绝对路径即可-->
<classPathEntry location="E:\MavenRepositoryForIdea\mysql\mysql-connector-java\5.1.6\mysql-connector-java-5.1.6.jar" />
<context id="testTables" targetRuntime="MyBatis3">
<commentGenerator>
<!-- 是否去除自动生成的注释 true:是 : false:否 -->
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://ip址:3306/baobaobaobao" userId="root"
password="root">
</jdbcConnection>
<!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 和
NUMERIC 类型解析为java.math.BigDecimal -->
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- targetProject:生成PO(实体类)类的位置 .\src为eclipse .\src\main\java 为idea 1-->
<javaModelGenerator targetPackage="baobaobaobao.entity"
targetProject=".\src\main\java">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
<!-- 从数据库返回的值被清理前后的空格 -->
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- targetProject:mapper映射文件生成的位置.\src为eclipse .\src\main\resourses 为idea 2-->
<sqlMapGenerator targetPackage="mapper"
targetProject=".\src\main\resources">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</sqlMapGenerator>
<!-- targetPackage:mapper接口生成的位置 .\src为eclipse .\src\main\java 为idea3-->
<javaClientGenerator type="XMLMAPPER"
targetPackage="baobaobaobao.dao"
targetProject=".\src\main\java">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</javaClientGenerator>
<!-- 指定数据库表 -->
<table schema="" tableName="EnUser"></table>
</context>
</generatorConfiguration>
启动mybitis逆向工程插件:
生成成功!
poExample类用于构造参数和条件.
七 分页插件
sql实现分页:
select * from table limit m,n ; 需要计算 m=第几页数减一乘以条数 n=条数
分页插件的作用是免去计算过程 直接转化为 sql分页语句
1、导入pagehelper依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.2</version>
</dependency>
2、Mybatis配置文件中添加分页插件
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!-- 分页参数合理化 -->
<property name="reasonable" value="true"/>
</plugin>
</plugins>

reasonable的配置:
reasonable:分页合理化参数,默认值为false。当该参数设置为 true 时,pageNum<=0 时会查询第一页,pageNum>pages(超过总数时),会查询最后一页。默认false 时,直接根据参数进行查询。
3、代码实现
@Test
public void queryAllPage() throws SQLException {
// 创建执行对象
SqlSession session = sqlSessionFactory.openSession();
UserDao userDao=session.getMapper(UserDao.class);
// 执行sql语句
List<UserEntity> list = userDao.queryAll();
// 拦截查询信息 指定分页信息 页码,记录数 不用计算
PageHelper.startPage(1, 3);
//配置分页信息
PageInfo<UserEntity> info=new PageInfo<UserEntity>(list);
System.out.println("总记录数:"+info.getTotal());
System.out.println("总页数:"+info.getPages());
System.out.println("每页条数:"+info.getPageSize());
System.out.println("当前页:"+info.getPageNum());
System.out.println("上一页:"+info.getPrePage());
System.out.println("下一页:"+info.getNextPage());
for (UserEntity user : info.getList()) {
System.out.println(user);
}
session.close();
}
八 多表关系与ResultMap和ResultType在使用中的区别
1 一对一,一对多,多对多关系:
2建表 sql
CREATE TABLE `dept` (
`deptno` int(9) NOT NULL COMMENT '部门编号',
`dname` varchar(50) DEFAULT NULL COMMENT '部门名称',
`loc` varchar(50) DEFAULT NULL COMMENT '部门地址',
PRIMARY KEY (`deptno`)
)COMMENT='部门信息表';
insert into `dept`(`deptno`,`dname`,`loc`) values
(20,'RESEARCH','DALLAS'),
(30,'SALES','CHICAGO');
CREATE TABLE `emp` (
`empno` int(4) NOT NULL COMMENT '雇员编号',
`ename` varchar(10) DEFAULT NULL COMMENT '雇员名称',
`job` varchar(9) DEFAULT NULL COMMENT '雇员岗位',
`sal` float(7,2) DEFAULT NULL COMMENT '雇员工资',
`dno` int(2) COMMENT '部门编号',
PRIMARY KEY (`empno`)
)COMMENT='雇员表';
insert into `emp`(`empno`,`ename`,`job`,`sal`,`dno`) values
(7369,'SMITH','CLERK',800.00,20),
(7370,'SCOTT','MANAGER',900.00,20),
(7372,'LILY','CLERK',820.00,30),
(7373,'LUCY','CLERK',830.00,30);
3 sql多表查询语句
--根据雇员id查询雇员及部门信息
select e.*,d.dname,d.loc from dept d,emp e where e.empno=7369 and d.deptno=e.dno;
--根据部门id查询部门信息及雇员信息
select e.*,d.dname,d.loc from dept d,emp e where d.deptno=20 and d.deptno=e.dno;
4 对应多表联合设计实体类
emp:
package baobaobaobao.entity;
public class Emp {
private Integer empno;
private String ename;
private String job;
private Float sal;
/**一对一关联部门对象*/
private Dept dno;
public Emp() {
super();
}
public Emp(Integer empno, String ename, String job, Float sal, Dept dno) {
this.empno = empno;
this.ename = ename;
this.job = job;
this.sal = sal;
this.dno = dno;
}
@Override
public String toString() {
return "Emp{" +
"empno=" + empno +
", ename='" + ename + '\'' +
", job='" + job + '\'' +
", sal=" + sal +
", dno=" + dno +
'}';
}
public Integer getEmpno() {
return empno;
}
public void setEmpno(Integer empno) {
this.empno = empno;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public Float getSal() {
return sal;
}
public void setSal(Float sal) {
this.sal = sal;
}
public Dept getDno() {
return dno;
}
public void setDno(Dept dno) {
this.dno = dno;
}
}
dept:
package baobaobaobao.entity;
public class Dept {
/**部门编号*/
public Integer deptno;
/**部门名称*/
public String dname;
/**部门地址*/
public String loc;
/**一对多关联雇员*/
public List<EmpEntity> emps;
public Integer getDeptno() {
return deptno;
}
public void setDeptno(Integer deptno) {
this.deptno = deptno;
}
public String getDname() {
return dname;
}
public void setDname(String dname) {
this.dname = dname;
}
public String getLoc() {
return loc;
}
public void setLoc(String loc) {
this.loc = loc;
}
public List<EmpEntity> getEmps() {
return emps;
}
public void setEmps(List<EmpEntity> emps) {
this.emps = emps;
}
@Override
public String toString() {
return "DeptEntity [deptno=" + deptno + ", dname=" + dname + ", loc=" + loc + ", emps=" + emps + "]";
}
}
5 映射文件
emp一对一:
<?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="baobaobaobao.dao.EmpMapper">
<resultMap id="BaseResultMap" type="baobaobaobao.entity.Emp">
<id column="empno" property="empno" />
<result column="ename" property="ename" />
<result column="job" property="job" />
<result column="sal" property="sal" />
<!-- <result column="dno" jdbcType="INTEGER" property="dno" />-->
<association column="dno" javaType="baobaobaobao.entity.Dept" property="dno" >
<id property="deptno" column="deptno"></id>
<result property="dname" column="dname"></result>
<result property="loc" column="loc"></result>
</association>
</resultMap>
<select id="MqueryEmpById" parameterType="int" resultMap="BaseResultMap">
select e.*,d.* from dept d,emp e where e.empno=#{empno} and d.deptno=e.dno
</select>
</mapper>
dept一对多:
<?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.offcn.mybatis.dao.DeptDao"><!-- 重要 -->
<!-- 部门map -->
<resultMap id="deptMap" type="DeptEntity">
<!-- 主键 -->
<id property="deptno" column="deptno"></id>
<result property="dname" column="dname"></result>
<result property="loc" column="loc"></result>
<collection property="emps" ofType="EmpEntity">
<!-- 配置雇员映射 -->
<id property="empno" column="empno"></id>
<result property="ename" column="ename"></result>
<result property="job" column="job"></result>
<result property="sal" column="sal"></result>
</collection>
</resultMap>
<!-- id查询雇员 -->
<select id="MqueryDeptById" parameterType="int" resultMap="deptMap">
select
d.*,e.* from dept d,emp e where d.deptno=#{deptno} and d.deptno=e.dno
</select>
</mapper>
配置mapper映射文件可能会发生的问题以及异常需要注意的地方:
① association 标签中无 javaType属性或者collection标签中无ofType属性,会发生以下异常:
org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: java.lang.NullPointerException
### The error may exist in mapper/EmpMapper.xml
### The error may involve baobaobaobao.dao.EmpMapper.MqueryEmpById
### The error occurred while handling results
### SQL: select e.*,d.* from dept d,emp e where e.empno=? and d.deptno=e.dno
### Cause: java.lang.NullPointerException
②javaType或者ofType的类名不完全,也会发生异常现象:
org.apache.ibatis.exceptions.PersistenceException:
### Error building SqlSession.
### The error may exist in mapper/EmpMapper.xml
### The error occurred while processing mapper_resultMap[BaseResultMap]_association[dno]
### Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. Cause: org.apache.ibatis.builder.BuilderException: Error resolving class. Cause: org.apache.ibatis.type.TypeException: Could not resolve type alias 'Dept'. Cause: java.lang.ClassNotFoundException: Cannot find class: Dept
6接口
public interface EmpMapper {
Emp MqueryEmpById(Integer empno);
}
public interface DeptMapper {
public DeptEntity MqueryDeptById(Integer deptno) throws SQLException;
}
7测试:
public class JunitMybatis {
public SqlSessionFactory sqlSessionFactory = null;
@Before
public void init() {
// 加载配置文件
Reader reader;
try {
reader = Resources.getResourceAsReader("mybatisConfig.xml");
// 创建构造器对象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
// 创建工厂
sqlSessionFactory = builder.build(reader);
// 创建执行对象
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void queryEmp() throws SQLException {
// 创建执行对象
SqlSession session = sqlSessionFactory.openSession();
EmpDao dao = session.getMapper(Emp.class);
EmpEntity emp = dao.queryEmpById(7370);
System.out.println(emp);
session.commit();
session.close();
}
@Test
public void queryDept() throws SQLException {
// 创建执行对象
SqlSession session = sqlSessionFactory.openSession();
DeptDao dao = session.getMapper(Dept.class);
DeptEntity dept = dao.queryDeptById(20);
System.out.println(dept);
// 单独取雇员信息
List<EmpEntity> list = dept.getEmps();
for (EmpEntity emp : list) {
System.out.println(emp);
}
session.commit();
session.close();
}
}
8多对多查询同一对多查询,不同的是 三张表联合查询
数据库设计如下:
老师数据表:
学生数据表:

第三方表:





如果需要通过学生id查询老师信息的话,在学生po类中添加老师LIST就可以了
public list<teacher> = new arraylist<teacher>()
九ResultMap和ResultType在使用中的区别
在使用mybatis进行数据库连接操作时对于SQL语句返回结果的处理通常有两种方式,一种就是resultType另一种就是resultMap,下面说下我对这两者的认识和理解
resultType:当使用resultType做SQL语句返回结果类型处理时,对于SQL语句查询出的字段在相应的pojo中必须有和它相同的字段对应,而resultType中的内容就是pojo在本项目中的位置。
因此对于单表查询的话用resultType是最合适的。但是,如果在写pojo时,不想用数据库表中定义的字段名称,也是可以使用resultMap进行处理对应的。
多表连接查询时,若是一对一的连接查询,那么需要新建一个pojo,pojo中包括两个表中需要查询出的所有的字段,这个地方的处理方式通常为创建一个
继承一个表字段的pojo,
再在里面添加另外一个表内需要查询出的字段即可。
若是一对多查询时,若是使用内连接查询,则很可能出现查询出的字段有重复。使用双重for循环嵌套处理即可。
resultMap:当使用resultMap做SQL语句返回结果类型处理时,通常需要在mapper.xml中定义resultMap进行pojo和相应表字段的对应。
十数据库连接池(先了解,与spring实际开发中一般不使用mybitis自带的数据源,需要引入外部数据源)
在mybitis配置文件中,不使用数据库连接池只需要配置四个属性
<property name="url" value="${jdbc.url}"></property>
<!-- name -->
<property name="username" value="${jdbc.name}"></property>
<!-- password -->
<property name="password" value="${jdbc.password}"></property>
<!-- driver class -->
<property name="driver" value="${jdbc.driver}"></property>

在mybitis配置文件中,使用数据库连接池只需要继续配置很多个属性
十一 mybitis二级缓存
二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。
一级缓存和二级缓存的区别:
1、一级缓存:基于PerpetualCache的HashMap本地缓存,其存储作用域为同一个SqlSession,当Session flush或close之后,该Session中的所有Cache就将清空。
2、二级缓存:与一级缓存其机制相同,默认也是采用PerpetualCache,HashMap存储,不同在于其存储作用域为Mapper(Namespace),并且可自定义存储源,如Ehcache。
3、对于缓存数据更新机制,当某一个作用域(一级缓存Session/二级缓存Namespaces)的进行了C/U/D操作后,默认该作用域下所有select中的缓存将被clear。
如果要实现MyBatis的二级缓存,一般来说有如下两种方式:
-
采用MyBatis内置的Cache机制。
-
采用三方Cache框架, 比如EhCache, OSCache等等。
下面是基于MyBatis的内置的一级和二级缓存测试:
1、一级缓存配置
默认是开启的,如果不想用缓存,直接在select节点中增加useCache="false"和flushCache="true"属性即可。如:
select user.id,user.userName,user.userAddress,article.id as aid,article.title,article.content from user,article where user.id=article.userid and user.id=#{id} flushCache:将其设置为true,无论语句什么时候被调用,都会导致缓存被清空。默认值:false。useCache:将其设置为true,将会导致本条语句的结果被缓存。默认值:true。
测试代码如下:
注意:此时在select节点中缓存是开启的。
package com.jsoft.testmybatis.test1;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import com.jsoft.testmybatis.inter.IUserOperation;
import com.jsoft.testmybatis.models.Article;
import com.jsoft.testmybatis.models.User;
public class App {
public static void main(String[] args) throws IOException {
InputStream inputStream = Resources.getResourceAsStream(“Configuration.xml”);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
try {
IUserOperation userOperation = session.getMapper(IUserOperation.class);
try {
System.out.println("第一次查询开始");
//List
List<User> users = userOperation.selectUsers("%");
for (User tempUser : users) {
System.out.println(tempUser.getUserAddress());
System.out.println(tempUser.getUserName());
}
System.out.println("第一次查询结束");
System.out.println("第二次查询开始");
//List
List<User> users2 = userOperation.selectUsers("%");
for (User tempUser : users2) {
System.out.println(tempUser.getUserAddress());
System.out.println(tempUser.getUserName());
}
System.out.println("第二次查询结束");
System.out.println("加入数据开始开始");
//Add
User addUser = new User();
addUser.setUserAddress("guangdong,guangzhou");
addUser.setUserName("eason");
addUser.setUserAge("80");
int addRetCount = userOperation.addUser(addUser);
session.commit();//必须提交事务,否则不会写入到数据库。如果session不commit,那么,数据就不会放入cache中。所以,只有commit后,才能取得。
System.out.println("增加数据影响的行数:" + addRetCount);
if (addUser.getId() > 0) {
System.out.println("增加数据成功,新增的id为:" + addUser.getId());
}
System.out.println("加入数据开始结束");
System.out.println("第三次查询开始");
//List
List<User> users3 = userOperation.selectUsers("%");
for (User tempUser : users3) {
System.out.println(tempUser.getUserAddress());
System.out.println(tempUser.getUserName());
}
System.out.println("第三次查询结束");
//强制刷新缓存
session.clearCache();
System.out.println("第四次查询开始");
//List
List<User> users4 = userOperation.selectUsers("%");
for (User tempUser : users4) {
System.out.println(tempUser.getUserAddress());
System.out.println(tempUser.getUserName());
}
System.out.println("第三次查询结束");
} catch (Exception e) {
// TODO: handle exception
session.rollback();//有异常时回滚数据
e.printStackTrace();
}
} finally {
session.close();//close之后缓存清空
}
}
}
结果如下:
可以看出,缓存生效了。
下面测试select节点中配置缓存关闭的情况,结果如下:
可以看出缓存去除了,全部都是真实查询。
其实上面的测试示例还少了一个Session2的测试,不然效果不佳。
2、二级缓存测试:
开启二级缓存,在XML配置文件中添加Cache节点即可,参考配置如下:
<cache eviction=“FIFO”
flushInterval=“60000”
size=“512”
readOnly=“true”/>
这个更高级的配置创建了一个 FIFO 缓存,并每隔 60 秒刷新,存数结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此在不同线程中的调用者之间修改它们会 导致冲突。可用的收回策略有, 默认的是 LRU:
LRU – 最近最少使用的:移除最长时间不被使用的对象。
FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
测试代码如下:
package com.jsoft.testmybatis.test1;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import com.jsoft.testmybatis.inter.IUserOperation;
import com.jsoft.testmybatis.models.Article;
import com.jsoft.testmybatis.models.User;
public class App2 {
public static void main(String[] args) throws IOException {
InputStream inputStream = Resources.getResourceAsStream(“Configuration.xml”);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
SqlSession session2 = sqlSessionFactory.openSession();
try {
IUserOperation userOperation = session.getMapper(IUserOperation.class);
IUserOperation userOperation2 = session.getMapper(IUserOperation.class);
try {
System.out.println(“Session1第一次查询开始”);
//List
List users = userOperation.selectUsers("%");
for (User tempUser : users) {
System.out.println(tempUser.getUserAddress());
System.out.println(tempUser.getUserName());
}
System.out.println(“Session1第一次查询结束”);
System.out.println("Session2第二次查询开始");
//List
List<User> users2 = userOperation2.selectUsers("%");
for (User tempUser : users2) {
System.out.println(tempUser.getUserAddress());
System.out.println(tempUser.getUserName());
}
System.out.println("Session2第二次查询结束");
System.out.println("Session1加入数据开始开始");
//Add
User addUser = new User();
addUser.setUserAddress("guangdong,guangzhou");
addUser.setUserName("eason");
addUser.setUserAge("80");
int addRetCount = userOperation.addUser(addUser);
session.commit();//必须提交事务,否则不会写入到数据库。如果session不commit,那么,数据就不会放入cache中。所以,只有commit后,才能取得。
System.out.println("增加数据影响的行数:" + addRetCount);
if (addUser.getId() > 0) {
System.out.println("增加数据成功,新增的id为:" + addUser.getId());
}
System.out.println("Session1加入数据开始结束");
System.out.println("Session1第三次查询开始");
//List
List<User> users3 = userOperation.selectUsers("%");
for (User tempUser : users3) {
System.out.println(tempUser.getUserAddress());
System.out.println(tempUser.getUserName());
}
System.out.println("Session1第三次查询结束");
//强制刷新缓存
session.clearCache();
System.out.println("Session1第四次查询开始");
//List
List<User> users4 = userOperation.selectUsers("%");
for (User tempUser : users4) {
System.out.println(tempUser.getUserAddress());
System.out.println(tempUser.getUserName());
}
System.out.println("Session1第四次查询结束");
System.out.println("Session2第五次查询开始");
//List
List<User> users5 = userOperation2.selectUsers("%");
for (User tempUser : users5) {
System.out.println(tempUser.getUserAddress());
System.out.println(tempUser.getUserName());
}
System.out.println("Session2第五次查询结束");
} catch (Exception e) {
// TODO: handle exception
session.rollback();//有异常时回滚数据
session2.rollback();//有异常时回滚数据
e.printStackTrace();
}
} finally {
session.close();//close之后缓存清空
session2.close();//close之后缓存清空
}
}
}
测试结果如下:
可以看出Cache不受Session的限制,且操作缓存的方法是一致的。
3、总结
1、映射语句文件中的所有select语句将会被缓存。
2、映射语句文件中的所有insert,update和delete语句会刷新缓存。
3、缓存会使用Least Recently Used(LRU,最近最少使用的)算法来收回。
4、缓存会根据指定的时间间隔来刷新。
5、每个缓存可以存储 1024 个列表或对象的引用(不管查询出来的结果是什么) 。
6、缓存将作为“读/写”缓存,意味着获取的对象不是共享的且对调用者是安全的。不会有其它的调用者或线程潜在修改。
7如果你的MyBatis使用了二级缓存,并且你的Mapper和select语句也配置使用了二级缓存,那么在执行select查询的时候,MyBatis会先从二级缓存中取输入,其次才是一级缓存,即MyBatis查询数据的顺序是:
二级缓存 ———> 一级缓存——> 数据库
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐



所有评论(0)