Mybatis
# Mybatis
MyBatis 是一个开源、轻量级的数据持久化框架,是 JDBC 和 Hibernate 的替代方案。MyBatis 内部封装了 JDBC,简化了加载驱动、创建连接、创建 statement 等繁杂过程,只需关注 SQL 语句即可
数据持久化是将内存中的 数据模型 转换为 存储模型,以及将 存储模型 转换为 内存中数据模型 的统称。 例如,文件的存储、数据的读取以及对数据表的增删改查等都是数据持久化操作
ORM 是一种数据持久化技术,它在对象模型和关系型数据库之间建立起对应关系,解决了实体类和数据库表映射的问题,并且提供了一种机制,通过 JavaBean 对象去操作数据库表中的数据。
- Object: java对象
- Relation: 关系,库中的表
- Mapping: 映射
参考文档:mybatis – MyBatis 3 (opens new window)
Mybatis 和 Hibernate区别
Mybateis | Hibernate | |
---|---|---|
学习门槛 | 低 | 高 |
查询语言 | sql | hql |
ORM架构 | 半映射 | 完整映射 |
资源损耗 | 低 | 高 |
查询自由度 | 高 | 低 |
移植兼容 | 差 | 好 |
总结
- Hibernate功能强大,数据库无关性好,O/R映射能力强。前提Hibernate要精通,否则用起来很累
- Hibernate 学习知识点多,精通门槛高。例如 怎么设计O/R映射,在性能和对象模型之间如何权衡取得平衡,以及怎样用好Hibernate方面需要经验和能力都很强才行
# 首次应用
前提:
- 引入
mybatis
架构依赖mysql-connector-java
底层连接依赖junit
代码测试- 数据库表中的列名称 与 实体类属性的名称 相同
项目结构
.
|
├── src
| ├── main
| | ├── java
| | | └── com
| | | ├── dao
| | | | ├── TeamDao
| | | | └── TeamDaoImpl
| | | ├── pojo
| | | | ├── Team
| | | | └── Team.xml
| | | └── utils
| | | └── MybatisUtil
| | └── resources
| | ├── log4j.properties
| | └── mybatis.xml
| test
| └── ...
└── pom.xml
引入依赖 ==pom.xml==
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.23</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency>
jar包说明:
- mybatis
- jdbc
- 测试环境
创建 ==Team实体类== 与 ==mybatis库team表数据==
CREATE TABLE `team` ( `teamId` int NOT NULL AUTO_INCREMENT COMMENT '球队ID', `teamName` varchar(50) DEFAULT NULL COMMENT '球队名称', `location` varchar(50) DEFAULT NULL COMMENT '球队位置', `createTime` date DEFAULT NULL COMMENT '球队建立时间', PRIMARY KEY (`teamId`) ) ENGINE=InnoDB AUTO_INCREMENT=1003 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
package com.pojo; import java.util.Date; public class Team { private Integer teamId; private String teamName; private String location; private Date createTme; /*省略setter和getter方法*/ @Override public String toString() { return "Team{" + "teamId=" + teamId + ", teamName='" + teamName + '\'' + ", location='" + location + '\'' + ", createTme=" + createTme + '}'; } }
配置 连接文件 ==mybatis.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> <!--配置 mybatis 环境--> <environments default="development"> <!--id:数据源的名称--> <environment id="development"> <!--事务类型:使用 JDBC 事务,使用 Connection 的提交和回滚--> <transactionManager type="JDBC"/> <!--数据源 dataSource:创建数据库 Connection 对象 type: POOLED 使用数据库的连接池 --> <dataSource type="POOLED"> <!--连接数据库的四大参数 注意 加载驱动是 MySQL8以上,否则 driver和url 都不一样,可参考学过的JDBC--> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis? useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT"/> <property name="username" value="root"/> <property name="password" value="root"/> </dataSource> </environment> </environments> <!-- 注册映射文件 --> <mappers> <mapper resource="com/pojo/Team.xml"/> </mappers> </configuration>
注意:
<dataSource>
标签 中主要配置数据库通信,必要的有 加载驱动/连接URL/账号/密码<mappers>.<mapper>.resource
属性 必须指定正确的映射文件(否则注册不了
配置 映射文件 ==Team.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"> <!-- namespace= "名称必须与映射的类的名字一致,是完全限定名" --> <mapper namespace="com.pojo.Team"> <!-- id="自定义名称,id不能重复;相当于dao中的方法名称" resultType="使用的要求:实体类中的属性名与表中的列名一致" --> <select id="findAll" resultType="com.pojo.Team"> select * from team </select> </mapper>
注意:
<mapper>.namespace
属性 必须映射正确的实体类(完全限定名<select>.resultType
属性 返回集中 一列记录实例的 对象/数据类型<select>.id
属性 用于Session指定指定SQL的操作(该属性不能存在重复值
配置 映射文件的扫描 ==pom.xml==
<build> <resources> <resource> <!--所有目录--> <directory>src/main/java</directory> <includes> <!--包括目录 .properties, .xml 文件都会扫描到!!--> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> </resources> <plugins> ··· </plugins> </build>
测试查询
@Test public void test() throws IOException { // 1. 读取 mybatis配置文件 Reader reader = Resources.getResourceAsReader("mybatis.xml"); // 2. 创建 SqlSessionFactoryd对象 , 目的 获取 sqlsession SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); // 3. 创建可执行SQL语句的 SqlSession SqlSession sqlSession = sqlSessionFactory.openSession(); // 4. 执行 SQL语句 List<Team> teamList = sqlSession.selectList("com.pojo.Team.findAll"); // 5. 遍历结果 System.out.println("遍历结果:"); teamList.forEach(System.out::println); // 6. 释放资源 sqlSession.close(); } /* 执行结果 遍历结果: Team{teamId=1, teamName='张三', location='上海', createTme=null} Team{teamId=2, teamName='李四', location='深圳', createTme=null} Team{teamId=3, teamName='王五', location='南京', createTme=null} Team{teamId=4, teamName='赵六', location='广州', createTme=null} Team{teamId=5, teamName='小七', location='南宁', createTme=null} */
# Mybatis对象
# Resources
==org.apache.ibatis.io.Resources类==
用于读取资源文件。有很多方法通过加载并解析资源文件,返回不同类型的 IO 流对象
# SqlSessionFactoryBuilder
==org.apache.ibatis.session.SqlSessionFactoryBuilder类==
SqlSessionFactory 的创建,需要使用 SqlSessionFactoryBuilder对象的build()方法 。事实上使用SqlSessionFactoryBuilder的原因是将SqlSessionFactory这个复杂对象的创建交由Builder来执行,也就是使用了建造者设计模式
建造者模式(又称生成器模式):是一种对象的创建模式。可以将一个产品的 内部表象 与 产品的生成过程 分割开来,从而可以使一个建造过程生成具有 不同的内部表象的产品(将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示),这样用户只需指定需要建造的类型就可以得到具体产品,而不需要了解具体的建造过程和细节。
在建造者模式中,角色分指导者(Director)与建造者(Builder): 用户联系指导者,指导者指挥建造者,最后得到产品,建造者模式可以强制实行 一种分步骤进行的建造过程
# SqlSessionFactory
==org.apache.ibatis.session.SqlSessionFactory接口==
创建 SqlSession 需要使用 SqlSessionFactory接口的 openSession()方法,该方法重载的数量较多,因此只需关注主要应用的以下三个传参数据即可:
- 事务处理: 在session作用域中 ,是否使用自动提交?(autoCommit)
- 数据库连接: 在 MyBatis中,是否使用自己提供的连接?(connection)
- 语句执行: 在 MyBatis中,是否复用 PreparedStatement 通道进行 批处理?
参数名 | 类型 | 说明 |
---|---|---|
autoCommit | boolean | true事务自动提交,否则关闭 |
connection | Connection | 应用自己提供的连接 |
execType | ExecutorType | ExecutorType枚举定义定义了三个值 SIMPLE:为每个语句的执行创建一个新的预处理语句 REUSE:执行器会复用预处理语句 BATCH:执行器会批量执行所有更新语句 |
**注意:**如果调用了无参的 openSession()方法,则该Session会默认具备以下特性:
- 事务作用域将会开启(不自动提交)
- 将由当前环境配置的 DataSource 实例中获取 Connection 对象 (DataSource指定的是 数据库的配置数据及创建数据库获取的连接)
- 事务隔离级别将会使用 驱动/数据源 的默认设置
- 预处理语句不会被复用,也不会批量处理更新
# SqlSessio
==org.apache.ibatis.session.SqlSession接口==
SqlSession接口对象 用于执行持久化操作。一个 SqlSession 对应着一次数据库会话
SqlSessio为 线程不安全的,所以每次数据库会话结束前,立马调用 close()方法 将其关闭。再次需要会话,再次创建
SQL语句写法以及形式有多种,而SqlSessio对象将它们归类封装成了方法 CURD(增删改查) 4中类型(支持自动装箱或包装类)、JavaBean、POJO 或 Map
<T> T selectOne(String statement, Object parameter)
<E> List<E> selectList(String statement, Object parameter)
<T> Cursor<T> selectCursor(String statement, Object parameter)
<K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey)
int insert(String statement, Object parameter)
int update(String statement, Object parameter)
int delete(String statement, Object parameter)
说明:
insert
、update
、delete
方法返回值为影响的行数selectOne
:返回一个 对象/null ;selectList
:返回多个 对象/null- 游标(Cursor)与列表(List)返回的结果相同,不同的是,游标借助迭代器实现了数据的惰性加载
- selectMap()方法 ,它会将返回的对象的其中一个属性作为 key值,将对象作为 value值
# MyBatis构架
- Mybatis.xml文件是mybatis框架的全局配置文件,配置了mybatis框架运行的环境等信息
- Mapperxx.xml是SQL的映射文件,文件中配置了所有的操作数据库的sql语句,这些文件需要在全局配置文件中加载
- 通过mybatis环境等配置信息构建SqlSessionFactroy ,相当于是产生连接池
- 由会话工厂创建SqlSession即会 (连接),操作数据库需要通过SqlSession进行的
- Mybatis底层自定义了Executor执行器的接口操作数据库,Executor接口有两个实现,一个基本的执行器,一个是缓存的执行器
- Mapped statement 也是mybatis框架一个底层的封装对象,包装了mybatis配置信息以及sql映射信息。Mapperxx.xml文件中的一个SQL语句对应一个Mapped statement对象,sql的id就是Mapped statement的id
- Mapped statement对SQL执行输入参数的定义,输入参数包括HashMap、基本类型、pojo,Executor通过Mapped statemen在执行SQL语句 前将输入java对象映射到sql语句中,执行完毕SQL之后,输出映射就是JDBC编码中的对preparedStatement 执行结果的定义
# MyBatis日志
日志能够更准确了解运行时的详细信息
实现步骤:
- 添加依赖
log4j
- 创建日志配置文件
log4j.properties
- 在
mybatis.xml
配置文件添加日志配置
依赖添加 (jar)
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
创建日志配置文件 log4j.properties
## Global logging configuration info warning error 选择日志呈现种类
log4j.rootLogger=DEBUG,stdout
## Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
在mybatis.xml
配置文件添加日志配置
<configuration>
<!-- 日志配置-->
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
·····
</configuration>
注意:
settings
标签 添加在该配置子标签中的第一个!
# MyBatis全局配置
之前应用的 全局配置文件 mybatis.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">
# 配置内容
MyBatis 行为设置和属性信息,全局配置文件的结构如下:
configuration(配置)
- properties(属性)
- settings(设置)
- typeAliases(类型别名)
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- environments(环境配置)
- environment(环境变量)
- transactionManager(事务管理器)
- dataSource(数据源)
- environment(环境变量)
- databaseIdProvider(数据库厂商标识)
- mappers(映射器)
以上的配置顺序一定一定要遵循顺序进行配置,否则会失效
# properties
properties标签 可在外部进行配置,并可以进行动态替换。properties标签 在 configuration标签里的 下一节点
- properties子元素配置
- 外部属性文件配置
以连接 数据库 的四个参数数据为例子
properties子元素配置
<?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>
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</properties>
····
<dataSource type="POOLED">
<!--连接数据库的四大参数
注意数据库版本使用的是 MySQL8以上,如果是mysql5的话,driver和url都不一样,参考学过的JDBC-->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
····
</configuration>
外部属性文件配置 jdbc.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT
jdbc.username=root
jdbc.password=root
<?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="jdbc.properties"/>
····
<dataSource type="POOLED">
<!--连接数据库的四大参数
注意数据库版本使用的是 MySQL8以上,如果是mysql5的话,driver和url都不一样,参考学过的JDBC-->
<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>
····
</configuration>
# settings
MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。例如:日志、等...(以上实例有应用就不赘述了)
<!--配置日志-->
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings
# typeAliases
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。typeAliases标签 在 settings标签 的下一节点
<?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>
···
<!-- 日志配置-->
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
<!-- 自定义别名-->
<typeAliases>
<!--对单个实体类-->
<typeAlias type="com.pojo.Team" alias="Team"/>
<!--批量定义别名:类似于扫描(首字母支持大小写)-->
<package name="com.pojo"/>
</typeAliases>
···
</configuration>
在应用于 映射文件 里 的 parameterType
、resultType
、...等属性
<!--查询所有-->
<select id="findAll" resultType="Team">
select * from team;
</select>
<!--添加-->
<insert id="add" parameterType="Team">
INSERT INTO `mybatis`.`team`(`teamName`, `location`, `createTime`) VALUES (#{teamName}, #{location}, #{createTime})
</insert>
<!--查询所有2-->
<select id="queryAll3" resultType="Team2">
select teamId 'team_id',teamName 'team_name',location,createTime from team
</select>
<!--查单列单条数据-->
<select id="queryTotal" resultType="int">
select count(teamId) from team
</select>
<!--查多列单条数据-->
<select id="queryMap" resultType="map">
select min(teamId) 'min',max(teamId) 'max' from team
</select>
其他别名
别名 | 映射的类型 |
---|---|
_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 |
object | Object |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
iterator | Iterator |
# Mappers
MyBatis 执行 SQL映射语句 前提需要告诉 MyBatis 映射文件路径在哪里,从而实现执行SQL语句
配置形式:
- 使用 相对于类路径的资源 引用
- 使用 映射器接口实现类 的完全限定类名
- 使用 包类的映射器接口实现全部注册为映射器(推荐)
<!-- 注册映射文件 -->
<mappers>
<!--
相对于类路径的资源 引用
使用相对于类路径的资源,从 classpath 路径查找文件
-->
<mapper resource="com/mapper/TeamMapper.xml"/>
<!--
使用的mapper接口的完全限定名
要求:接口和映射文件同包同名
-->
<mapper class="com.mapper.GameRecordMapper"/>
<!--
指定包下的 所有Mapper接口
注意:此种方法要求 Mapper接口名称和 mapper 映射文件名称相同,且在同一个目录中。
-->
<package name="com.mapper"/>
</mappers>
# Mapper动态代理
前面定义的 Dao接口和Dao实现类 没有实质性的工作意义,因此我们弃用Dao,可通过 SqlSession 的相关 API 定位到映射文件 mapper 中相应 id 的 SQL 语句,真正对 DB 进行操作的工作其实是由框架通过 mapper 中的 SQL 完成的,该形式被称为 ==Mapper接口 的动态代理方式==
要点说明:
- Mapper 动态代理方式无需实现 Dao 接口。接口是由 MyBatis 结合映射文件自动生成的动态代理实现的
- Mapper(映射) 文件里 sql语句中的标签id值 必须对应 接口中的方法名 一致
- resources配置文件夹,在idea开发工具不能跨级进行创建,必须手动逐级创建路径
- Mapper(映射) 在resources配置 文件夹中的路径必须 与 接口中的完全限定名 一致
- SqlSession对象 需要通过
getMapper(Class<T> type)
方法 获取代理对象,可获取指定接口的实现类对象- Mapper(映射) 文件里 mapper标签的
namespace
属性值 需要指定 接口的完全限定名- 每次添加映射 都需要去
mybatis.xml
的 mappers标签 进行添加注册映射文件- 进行 增删改 时需要
MybatisUtil
工具类 获取SqlSession对象 进行提交
实例应用
项目结构
.
|
├── src
| ├── main
| | ├── java
| | | └── com
| | | ├── mapper
| | | | └── TeamMapper
| | | ├── pojo
| | | | └── Team
| | | └── utils
| | | └── MybatisUtil
| | └── resources
| | ├── log4j.properties
| | ├── mybatis.xml
| | └── com
| | └── mapper
| | └── TeamMapper.xml
| test
| └── ...
└── pom.xml
创建 ==TeamMapper接口==
package com.mapper;
import com.pojo.Team;
import java.util.List;
public interface TeamMapper {
List<Team> findAll();
Team queryById(int id);
Team queryByName(String name);
int add(Team team);
int update(Team newTeam);
int delById(int id);
int delByName(String name);
}
创建 ==TeamMapper.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">
<!--namespace="名称必须与映射的类的名字一致,是完全限定名"-->
<mapper namespace="com.mapper.TeamMapper">
<!--
id="自定义名称,id不能重复;相当于dao中的方法名称"
resultType="使用的要求:实体类中的属性名与表中的列名一致"
-->
<!-- 查询所有-->
<select id="findAll" resultType="com.pojo.Team">
select * from team;
</select>
<!-- 查询指定-->
<select id="queryById" resultType="com.pojo.Team">
select * from team where teamId=#{teamId}
</select>
<select id="queryByName" resultType="com.pojo.Team">
select * from team where teamName=#{teamName}
</select>
<!--
parameterType="指定对象作为参数"
#{对象属性名}
-->
<!-- 添加数据-->
<insert id="add" parameterType="com.pojo.Team">
INSERT INTO `mybatis`.`team`(`teamName`, `location`, `createTime`) VALUES (#{teamName}, #{location}, #{createTime})
</insert>
<!-- 修改数据-->
<update id="update" parameterType="com.pojo.Team">
UPDATE `team` SET teamName=#{teamName},location=#{location} WHERE teamId=#{teamId}
</update>
<!-- 删除数据-->
<delete id="delById" parameterType="com.pojo.Team">
DELETE FROM `mybatis`.`team` WHERE `teamId` = #{id}
</delete>
<delete id="delByName" parameterType="com.pojo.Team">
DELETE FROM `mybatis`.`team` WHERE `teamName` = #{name}
</delete>
</mapper>
在 ==mybatis.xml配置文件== 中注册映射文件
<configuration>
·····
<!-- 注册映射文件 -->
<mappers>
·····
<mapper resource="com/mapper/TeamMapper.xml"/>
</mappers>
</configuration>
测试:
import com.mapper.TeamMapper;
import com.pojo.Team;
import com.utils.MybatisUtil;
import org.junit.Test;
import java.util.Date;
import java.util.List;
public class TeamMapperTest {
//前提 接口的方法名 与 映射sql 标签的id值 相同!!!
private TeamMapper teamMapper = MybatisUtil.getSqlSession().getMapper(TeamMapper.class);
@Test
public void findAll(){
List<Team> all = teamMapper.findAll();
all.forEach(System.out::println);
}
@Test
public void queryById(){
Team team = teamMapper.queryById(2);
System.out.println("team : " + team);
}
@Test
public void queryByName(){
Team team = teamMapper.queryByName("火箭");
System.out.println("team : " + team);
}
@Test
public void add(){
Team team = new Team("公牛","洛杉矶",new Date());
int add = teamMapper.add(team);
MybatisUtil.getSqlSession().commit();
System.out.println("add : " + add);
}
@Test
public void update() {
Team team = teamMapper.queryById(1009);
team.setTeamName("老鸽");
team.setLocation("南京");
int update = teamMapper.update(team);
MybatisUtil.getSqlSession().commit();
System.out.println("update : " + update);
}
@Test
public void delById() {
int i = teamMapper.delById(1009);
MybatisUtil.getSqlSession().commit();
System.out.println("i : " + i);
}
@Test
public void delByName() {
int i = teamMapper.delByName("公牛");
MybatisUtil.getSqlSession().comit();
System.out.println("i : " + i);
}
}
# parameterType输入
# 传递多个参数
parameterType 指定值是接口中方法参数的类型,类型必须是完全限定名 或 别名。该属性非必须,因为Mybatis框架能自行判断具 体传入语句的参数
Mybatis 提供以下 3 种方式,实现给映射器传递多个参数:
- 方法直接传递参数
- 使用注解传递参数
- 使用 Map传递参数
应用说明:
方法直接传递参数 进行传递 不同mybatis版本传递参数 在sql应用不一样
- mybatis3.3之前:使用 #{0}、#{1}、...
- mybatis3.3之后:使用 #{arg0},#{arg1}、... 或者 #{param1}、#{param2}、...
sql语句中 的 大小于号 不能使用 ,需要 转义符
<
:<
>
:>
使用@Param注解后,只能应用 自定的注解名称
多参数应用
修改 ==TeamMapper接口== 添加sql执行的方法
package com.mapper;
import com.pojo.Team;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
public interface TeamMapper {
····
List<Team> queryByRange1(int min , int max);
List<Team> queryByRange2(int min , int max);
List<Team> queryByRange3(@Param("min") int min ,@Param("max") int max);
List<Team> queryByRange4(Map<String,Object> map);
}
修改 ==TeamMapper.xml映射文件== 添加sql语句
<!--arg 应用-->
<select id="queryByRange1" resultType="com.pojo.Team">
select * from team where teamId>=#{arg0} and teamId <= ${arg1}
</select>
<!--param 应用-->
<select id="queryByRange2" resultType="com.pojo.Team">
select * from team where teamId>=#{param1} and teamId <= ${param2}
</select>
<!--注解别名 应用-->
<select id="queryByRange3" resultType="com.pojo.Team">
select * from team where teamId>=#{min} and teamId <= ${max}
</select>
<!--Map 应用-->
<!-- 传参 #{} Map集合中的 Key保持一致-->
<select id="queryByRange4" resultType="com.pojo.Team">
select * from team where teamId>=#{min} and teamId <= ${max}
</select>
测试:
import com.mapper.TeamMapper;
import com.pojo.Team;
import com.utils.MybatisUtil;
import org.junit.Test;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
//多参数传递应用
public class multiParameterTest {
private TeamMapper teamMapper = MybatisUtil.getSqlSession().getMapper(TeamMapper.class);
//arg 应用
@Test
public void test01() {
List<Team> teamList = teamMapper.queryByRange1(1 , 6);
teamList.forEach(team -> System.out.println(team));
}
//param 应用
@Test
public void tset02() {
List<Team> teamList = teamMapper.queryByRange2(1 , 6);
teamList.forEach(team -> System.out.println(team));
}
//接口参数名 应用 (需在接口方法中的参数定义名)
@Test
public void tset03() {
List<Team> teamList = teamMapper.queryByRange3(1 , 6);
teamList.forEach(team -> System.out.println(team));
}
//Map传递 应用
@Test
public void tset04() {
Map<String,Object> map = new HashMap<>();
map.put("min",1);
map.put("max",6);
List<Team> teamList = teamMapper.queryByRange4(map);
teamList.forEach(team -> System.out.println(team));
}
}
# #{} 和 ${} 区别
#{}
:表示一个占位符,通知Mybatis 使用实际的参数值代替
${}
:表示字符串原样替换,通知Mybatis 使用 $
包含的“字符串”替换所在位置
示例:
配置接口方法
public interface TeamMapper {
···
Team queryByType(
@Param("type")
String type,
@Param("data")
Object data);
}
映射文件
<select id="queryByType" resultType="com.pojo.Team">
select * from team where ${type}=#{data}
</select>
测试(前提库有相应信息)
@Test
public void queryByType() {
//teamId、teamName
Team team1 = teamMapper.queryByType("teamId",1017);
Team team2 = teamMapper.queryByType("teamName","Sanscan12");
System.out.println("team1 : " + team1);
System.out.println("team2 : " + team2);
}
# resultType输出
resultType 执行 sql 得到 ResultSet 转换的类型,使用类型的完全限定名 或 别名。如果返回的是集合,设置的是集合元素的类型,而不是集合本身。resultType 和 resultMap, 不能同时使用。
可通过以下方式进行映射输出:
- 输出java基本属性类型
- 输出Map类型
- 输出pojo类型
- 输出自定义resultMap类型
应用说明:
- resultMap 专门用于数据库中的列和实体类不匹配的情况下使用
- resultMap 是 自己编写表中的列名 与 实体类中的属性 的映射(他们不匹配的前提下需要自行匹配映射)
应用
修改 ==TeamMapper接口== 添加sql执行的方法
package com.mapper;
import com.pojo.Team;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
public interface TeamMapper {
····
int queryTotal();
Map<String,Object> queryMap();
List<Map<String, Object>> queryMapList();
List<Team> queryAll2();
}
修改 ==TeamMapper.xml映射文件== 添加sql语句
<!-- 查单列单条数据 基本类型输出-->
<select id="queryTotal" resultType="java.lang.Integer">
select count(teamId) from team
</select>
<!-- 查多列单条数据 Map类型输出-->
<select id="queryMap" resultType="java.util.HashMap">
select min(teamId) 'min',max(teamId) 'max' from team
</select>
<!-- 查多列多条数据 List<Map>类型输出-->
<select id="queryMapList" resultType="java.util.HashMap">
select teamName,location from team
</select>
<!--
resultMap是 自己编写表中的列名 与 实体类中的属性 的映射(他们不匹配的前提下需要自行匹配映射)
id: resultMap的名称,要求唯一
type: 期待要映射为java的类型
id 主键列 ; result 其余列
column: 数据库中的列名,不区分大小写
property: 实体类中的属性名,区分大小写
javaType: 实体类中对应的属性类型
jdbcType: 数据库中column类型(一般忽略)
-->
<!-- resultMap数据类型 自定义映射输出-->
<select id="queryAll2" resultMap="baseResultMap">
select * from team
</select>
<resultMap id="baseResultMap" type="com.pojo.Team">
<id column="teamId" property="teamId" javaType="java.lang.Integer"/>
<result column="teamName" property="teamName" javaType="java.lang.String"/>
<result column="location" property="location" javaType="java.lang.String"/>
<!-- <result column="createTime" property="createTime" javaType="java.util.Date"/>-->
</resultMap>
测试
import com.mapper.TeamMapper;
import com.pojo.Team;
import com.utils.MybatisUtil;
import org.junit.Test;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
//多参数传递应用
public class multiParameterTest {
private TeamMapper teamMapper = MybatisUtil.getSqlSession().getMapper(TeamMapper.class);
//映射输出形式
//查单列单条数据
@Test
public void queryTotal() {
int i = teamMapper.queryTotal();
System.out.println("i : " + i);
}
//查多列单条数据
@Test
public void queryMap() {
Map<String, Object> stringObjectMap = teamMapper.queryMap();
System.out.println(stringObjectMap);
}
//查多列多条数据
@Test
public void queryMapList() {
List<Map<String, Object>> mapList = teamMapper.queryMapList();
for (Map<String, Object> stringObjectMap : mapList) {
System.out.println(stringObjectMap);
}
}
//处理自定义类型数据
@Test
public void queryAll2() {
List<Team> teamList = teamMapper.queryAll2();
teamList.forEach(team -> System.out.println(team));
}
}
# 动态SQL
动态 SQL 是 MyBatis 的强大特性之一。在 JDBC 或其它类似的框架中,需要手动拼接 SQL 语句,避免了 在不同条件下拼接 SQL 语句的困难
动态SQL应用到的元素
<choose>
:多条件分支<when>
:判断是否满足 test (boolean):表达式<otherwise>
:都不满足条件
<foreach>
:遍历语句 collection (colection):遍历集合 item (Object):迭代对象 separator (String):迭代分割符 open (String):循环开头 close (String):循环结尾<where>
:查询约束<if>
:是否添加语句test (boolean):表达式
<set>
:编辑约束<if>
:是否添加语句test (boolean):表达式
test属性:多个表达式定义的时候 需要使用
and
、or
应用
应用前提:
- 有库信息
- 映射文件已注册(扫描包)
表结构
字段名 | 类型 |
---|---|
deptno | int |
dname | varchar |
loc | varchar |
创建 ==com.pojo.Dept实体对象==
public class Dept {
private int deptno;
private String dname;
private String loc;
//省略 get、set、toString、构造 方法
}
创建 ==com.mapper.DeptMapper接口==
public interface DeptMapper {
// CRUD
int add(@Param("depts") Collection<Dept> depts);
int del(Dept dept);
int update(Dept dept);
List<Dept> findByLike(Dept dept);
}
创建 ==DeptMapper.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">
<!-- namespace= "名称必须与映射的类的名字一致,是完全限定名" -->
<mapper namespace="com.sans.mapper.DeptMapper">
<!-- CRUD 动态SQL
列表添加 for
删除 where=>if
修改 set=>if
查 choose=>...
-->
<!-- 列表添加 for
sql示例:INSERT INTO dept(dname, loc) VALUES (?,?) , (?,?) [, ...]
标签属性说明:
collection:指定集合
item:集合中的每个迭代对象
separator:每次迭代之间的分隔符
open:循环语句 以指定字符为 开头
close:循环语句 以指定字符为 结束
-->
<insert id="add" parameterType="collection">
INSERT INTO dept(dname, loc) VALUES
<foreach collection="depts" item="dept" separator=",">
(#{dept.dname},#{dept.loc})
</foreach>
</insert>
<!-- 删除 where=>if
sql示例1:DELETE FROM dept WHERE deptno = ?
sql示例2:DELETE FROM dept WHERE dname = ?
sql示例3:DELETE FROM dept WHERE deptno = ? AND dname = ?
-->
<delete id="del" parameterType="com.sans.pojo.Dept" >
DELETE FROM dept
<where>
<if test="deptno != 0">
AND deptno = #{deptno}
</if>
<if test="dname != null and dname != ''">
AND dname = #{dname}
</if>
</where>
</delete>
<!-- 修改 set=>if
sql示例1:UPDATE dept SET dname = ? where deptno = ?
sql示例2:UPDATE dept SET loc = ? where deptno = ?
sql示例3:UPDATE dept SET dname = ? , loc = ? where deptno = ?
-->
<update id="update" parameterType="com.sans.pojo.Dept" >
UPDATE dept
<set>
<if test="dname != null and dname != ''">
dname = #{dname}
</if>
<if test="loc != null and loc != ''">
<if test="dname != null and dname != ''">,</if>
loc = #{loc}
</if>
</set>
where deptno = #{deptno}
</update>
<!-- 查 choose=>...
sql示例1:select * from dept where 1=1 and dname like '%会%'
sql示例2:select * from dept where 1=1 and loc like '%总%'
sql示例3:select * from dept where 1=1 and deptno = ?
标签说明:
choose:指定分支
when:条件分支节点
otherwise:都未满足条件
-->
<select id="findByLike" parameterType="com.sans.pojo.Dept" resultType="com.sans.pojo.Dept">
select * from dept where 1=1
<choose>
<when test="dname != null and dname != ''">
and dname like '%${dname}%'
</when>
<when test="loc != null and loc != ''">
and loc like '%${loc}%'
</when>
<otherwise>
and deptno = #{deptno}
</otherwise>
</choose>
</select>
<select id="findAll" resultType="com.sans.pojo.Dept">
select * from dept
</select>
</mapper>
测试
package com.sans;
import com.sans.mapper.DeptMapper;
import com.sans.pojo.Dept;
import com.sans.utils.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.*;
public class Demo {
/** SQL动态查询
* 列表添加 for
* 删除 where => if
* 修改 set => if
* 查 choose => .
*/
SqlSession session = MyBatisUtil.getSession();
@Test
public void addTest() {
DeptMapper mapper = session.getMapper(DeptMapper.class);
/** 集合 测试
* 意图 : foreach标签 指定集合类型遍历区别
* - List 兼容
* - Set 兼容
* - Map 不兼容
*/
// List 测试
// List<Dept> list = new ArrayList<>();
// list.add(new Dept("会计部1","汇总"));
// list.add(new Dept("会计部2","汇总"));
// list.add(new Dept("会计部3","汇总"));
// set 测试
Set<Dept> set = new HashSet<>();
set.add(new Dept("会计部1","汇总"));
// set.add(new Dept("会计部2","汇总"));
// set.add(new Dept("会计部3","汇总"));
int add = mapper.add(set);
// session.commit();
System.out.println("add : " + add);
}
@Test
public void delTest() {
DeptMapper mapper = session.getMapper(DeptMapper.class);
Dept dept = new Dept();
// dept.setDeptno(189);
dept.setDname("会计部1");
int del = mapper.del(dept);
// session.commit();
System.out.println("del : " + del);
}
@Test
public void updateTest() {
DeptMapper mapper = session.getMapper(DeptMapper.class);
Dept d = new Dept();
d.setDeptno(5);
d.setDname("张三");
d.setLoc("李四");
int update = mapper.update(d);
// session.commit();
System.out.println("update : " + update);
}
@Test
public void findByTypeTest() {
DeptMapper mapper = session.getMapper(DeptMapper.class);
Dept d = new Dept();
// d.setDname("会");
// d.setLoc("总");
d.setDeptno(5);
mapper.findByLike(d).forEach(System.out::println);
}
}
# MyBatis映射关系
在MySQL中,当两表之间存在着主从关系,那么从表有个外键 对应 主表的主键 !
以下是可能出现映射关系的情况:
- 对一 映射关系
- 对多 映射关系
# 对一 映射关系
对一关系映射形式:
- 一对一
- 一对多
一对多映射接收封装方式引用 Mapper映射封装(需要搭配扩展封装 一方对象
实现应用
前提:
- 分清对象的 主从关系 ,特别是查询的主对象
- 全局配置文件 已注册 映射文件
- 反向生成映射配置
- 确定好数据表
数据库 表
Dept 部门表
字段名 | 类型 | 说明 | 关系键 |
---|---|---|---|
deptno | int | id | key |
dname | varchar | 部门名称 | |
loc | varchar | 部门地址 |
Emp 员工表
字段名 | 类型 | 说明 | 关系键 |
---|---|---|---|
empno | int | id | key |
ename | varchar | 员工名 | |
job | varchar | 职位 | |
mgr | int | ||
hiredate | datetime | 入职时间 | |
sal | decimal | 出售 | |
comm | decimal | 佣金 | |
deptno | int | 部门id | dept.deptno |
jobno | int | 职位id | job.jobno |
一对多示例
实体类Emp
public class Emp {
private Integer empno;
private String ename;
private String job;
private Integer mgr;
private Date hiredate;
private double sal;
private double comm;
private Integer deptno;
private Integer jobno;
private Dept dept;
// 省略 get、set、toString、构造 方法
}
实体类Dept
public class Dept {
private Integer deptno;
private String dname;
private String loc;
// 省略 get、set、toString、构造 方法
}
映射接口EmpMapper
public interface EmpMapper {
// .... 省略 反向生成的 标配CRUD操作
// 内连接多查询 搭配 官方约束容器查询
List<Emp> selectByExampleJoinDept(EmpExample example);
}
映射文件EmpMapper.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="com.sans.mapper.EmpMapper">
<!-- .... 省略非关键代码 -->
<!-- 反向生成 约束容器的代码 -->
<sql id="Example_Where_Clause">
<where>
<foreach collection="oredCriteria" item="criteria" separator="or">
<if test="criteria.valid">
<trim prefix="(" suffix=")" prefixOverrides="and">
<foreach collection="criteria.criteria" item="criterion">
<choose>
<when test="criterion.noValue">
and ${criterion.condition}
</when>
<when test="criterion.singleValue">
and ${criterion.condition} #{criterion.value}
</when>
<when test="criterion.betweenValue">
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
</when>
<when test="criterion.listValue">
and ${criterion.condition}
<foreach collection="criterion.value" item="listItem" open="(" close=")"
separator=",">
#{listItem}
</foreach>
</when>
</choose>
</foreach>
</trim>
</if>
</foreach>
</where>
</sql>
<!-- .... 省略非关键代码 -->
<!-- 手配Map映射 扩展对象进行封装 -->
<!-- <resultMap>标签 属性说明
id: 自定义Map映射结果集名
type:指定查询的主对象
-->
<resultMap id="EmpExpandMap" type="com.sans.pojo.Emp">
<!-- 查询结果及映射 属性/字段 配置
column:指定数据库查询结果的字段
property:指定实体类属性对应的字段
jdbcType:属性/字段 类型 (注意语法
-->
<id column="empno" property="empno" jdbcType="INTEGER"/>
<result column="ename" property="ename" jdbcType="VARCHAR"/>
<result column="job" property="job" jdbcType="VARCHAR"/>
<result column="mgr" property="mgr" jdbcType="INTEGER"/>
<result column="hiredate" property="hiredate" jdbcType="TIMESTAMP"/>
<result column="sal" property="sal" jdbcType="DECIMAL"/>
<result column="comm" property="comm" jdbcType="DECIMAL"/>
<result column="deptno" property="deptno" jdbcType="INTEGER"/>
<result column="jobno" property="jobno" jdbcType="INTEGER"/>
<!-- <association>标签 属性说明
property:指定查询的从对象
javaType:指定从对象的实体对象
-->
<association property="dept" javaType="com.sans.pojo.Dept">
<id column="deptno" property="deptno" jdbcType="INTEGER"/>
<result column="dname" property="dname" jdbcType="VARCHAR"/>
<result column="loc" property="loc" jdbcType="VARCHAR"/>
</association>
</resultMap>
<!-- 应用 Emp扩展类
sql实例1:select emp.* , dept.* from emp, dept where emp.deptno = emp.deptno
sql实例2:select emp.* , dept.* from emp, dept WHERE ( ename like ? ) and emp.deptno = emp.deptno
-->
<select id="selectByExampleJoinDept" resultMap="EmpExpandMap"
parameterType="com.sans.pojo.EmpExample">
select emp.* , dept.*
from emp, dept
<!-- where 约束 -->
<if test="_parameter != null">
<include refid="Example_Where_Clause"/>
<!-- 筛选 -->
and emp.deptno = emp.dept
</if>
<!-- 筛选 -->
<if test="_parameter == null">
where emp.deptno = emp.deptno
</if>
</select>
</mapper>
测试
/** 一对多测试
* 由于 一对一 较于简单 且应用场景不多,因此不写了
*
* 一对多是 主要应用测试
*/
@Test
public void oneToMany() {
EmpMapper mapper = MyBatisUtil.getSession().getMapper(EmpMapper.class);
EmpExample example = new EmpExample();
EmpExample.Criteria criteria = example.createCriteria();
criteria.andEnameLike("%j%");
List<Emp> emps = mapper.selectByExampleJoinDept(example);
emps.forEach(o -> {
System.out.println(o+" => "+o.getDept());
});
}
# 对多映射关系
多对多关系之间的建立需要 第三方表的建立才能进行联系
实现应用
以下的 多对多形式分别用了 中间表查询 和 对多关系查询 两种方式
数据库 表
Dept 部门表
字段名 | 类型 | 说明 | 关系键 |
---|---|---|---|
deptno | int | id | key |
dname | varchar | 部门名称 | |
loc | varchar | 部门地址 |
Emp 员工表
字段名 | 类型 | 说明 | 关系键 |
---|---|---|---|
empno | int | id | key |
ename | varchar | 员工名 | |
job | varchar | 职位 | |
mgr | int | ||
hiredate | datetime | 入职时间 | |
sal | decimal | 出售 | |
comm | decimal | 佣金 | |
deptno | int | 部门id | dept.deptno |
jobno | int | 职位id | job.jobno |
Job 职位表
字段名 | 类型 | 说明 | 关系键 |
---|---|---|---|
jobno | int | id | key |
janme | varchar | 职位名 | |
remark | varcahr | 备注 |
这三张表 的关系:可以看似为 多个职位对应多个部门 ,而他们的练习包含有 员工表
实体类Emp
public class Emp {
private Integer empno;
private String ename;
private String job;
private Integer mgr;
private Date hiredate;
private double sal;
private double comm;
private Integer deptno;
private Integer jobno;
// dept 多对一关系
private Dept beanDept;
// job 多对一关系
private Job beanJob;
// 省略 get、set、toString、构造 方法
}
实体类Dept
public class Dept {
private Integer deptno;
private String dname;
private String loc;
private List<Job> jobs;
// 省略 get、set、toString、构造 方法
}
实体类Job
public class Job {
private Integer jobno;
private String jname;
private String remark;
private List<Dept> dpets;
// 省略 get、set、toString、构造 方法
}
EmpMapper接口
public interface EmpMapper {
// .... 省略 反向生成的 标配CRUD操作
// 通过中间表进行查询 两表信息
List<Emp> selectByExampleJoinDateAndJob(EmpExample example);
}
EmpMapper.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="com.sans.mapper.EmpMapper">
<resultMap id="BaseResultMap" type="com.sans.pojo.Emp">
<id column="empno" property="empno" jdbcType="INTEGER"/>
<result column="ename" property="ename" jdbcType="VARCHAR"/>
<result column="job" property="job" jdbcType="VARCHAR"/>
<result column="mgr" property="mgr" jdbcType="INTEGER"/>
<result column="hiredate" property="hiredate" jdbcType="TIMESTAMP"/>
<result column="sal" property="sal" jdbcType="DECIMAL"/>
<result column="comm" property="comm" jdbcType="DECIMAL"/>
<result column="deptno" property="deptno" jdbcType="INTEGER"/>
<result column="jobno" property="jobno" jdbcType="INTEGER"/>
</resultMap>
<!-- .... 省略非关键代码 -->
<!-- 通过中间表查 两表的关系 -->
<resultMap id="EmpAndDeptAndJobMap" type="com.sans.pojo.Emp" extends="BaseResultMap">
<association property="beanDept" javaType="com.sans.pojo.Dept">
<id column="deptno" property="deptno" jdbcType="INTEGER"/>
<result column="dname" property="dname" jdbcType="VARCHAR"/>
<result column="loc" property="loc" jdbcType="VARCHAR"/>
</association>
<association property="beanJob" javaType="com.sans.pojo.Job">
<id column="jobno" property="jobno" jdbcType="INTEGER"/>
<result column="jname" property="jname" jdbcType="VARCHAR"/>
<result column="remark" property="remark" jdbcType="VARCHAR"/>
</association>
</resultMap>
<!-- 多对多 -->
<select id="selectByExampleJoinDateAndJob" resultMap="EmpAndDeptAndJobMap"
parameterType="com.sans.pojo.EmpExample">
SELECT
emp.*,dname,loc , jname,remark
FROM
dept,job,emp
<if test="_parameter != null">
<include refid="Example_Where_Clause"/>
AND dept.deptno = emp.deptno AND job.jobno = emp.jobno
</if>
<if test="_parameter == null">
WHERE dept.deptno = emp.deptno AND job.jobno = emp.jobno
</if>
</select>
</mapper>
DeptMapper
public interface DeptMapper {
// .... 省略 反向生成的 标配CRUD操作
// 直接多对多 查询
List<Dept> DeptAndJobByExample(DeptExample example);
}
DeptMapper.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="com.sans.mapper.DeptMapper">
<resultMap id="BaseResultMap" type="com.sans.pojo.Dept">
<id column="deptno" property="deptno" jdbcType="INTEGER"/>
<result column="dname" property="dname" jdbcType="VARCHAR"/>
<result column="loc" property="loc" jdbcType="VARCHAR"/>
</resultMap>
<!-- .... 省略非关键代码 -->
<!--
<BaseResultMap>.extends: 继承已有的属性
collection标签 属性说明
property:实体类中 数据集的属性名
javaType:实体类中 属性的类型
ofType:实体类中的 集合泛型类型
子标签指定的都是 实体对象单条记录中的属性
-->
<resultMap id="DeptAndJobsMap" type="com.sans.pojo.Dept" extends="BaseResultMap">
<collection property="jobs" javaType="java.util.ArrayList" ofType="com.sans.pojo.Job">
<id column="jobno" property="jobno" javaType="int"/>
<result column="jname" property="jname" javaType="String"/>
</collection>
</resultMap>
<!-- 查询部门涉及到的职位有哪些
sql示例1:SELECT emp.*,dname,loc , jname,remark FROM dept , job , emp WHERE
dept.deptno = emp.deptno AND job.jobno = emp.jobno
sql示例2:SELECT emp.*,dname,loc , jname,remark FROM dept , job , emp WHERE
( dname like ? ) AND dept.deptno = emp.deptno AND job.jobno = emp.jobno
-->
<select id="DeptAndJobByExample" resultMap="DeptAndJobsMap"
parameterType="com.sans.pojo.DeptExample">
SELECT emp.*,dname,loc , jname,remark FROM
dept , job , emp
<if test="_parameter != null">
<include refid="Example_Where_Clause"/>
AND dept.deptno = emp.deptno AND job.jobno = emp.jobno
</if>
<if test="_parameter == null">
WHERE dept.deptno = emp.deptno AND job.jobno = emp.jobno
</if>
</select>
</mapper>
JobMapper
JobMapper.xml
这两个用于铺垫使用,反向生成后,未更变!
测试
public class AnyToMany {
/** 对多关系
* 两表的对多关系 需要 第三方 中间表建立关系,因此必须包含有中间表
* 查询方式:
* - 以中间表作为主表 进行对两表 连接查询
* - 以对多的任意一个为主表 进行对多 连接查询
*/
// 中间表作为主表 进行连接查询
@Test
public void brokerOfFirstTableTest() {
EmpMapper mapper = MyBatisUtil.getSession().getMapper(EmpMapper.class);
// 配置约束容器
EmpExample example = new EmpExample();
EmpExample.Criteria criteria = example.createCriteria();
criteria.andEnameLike("%j%");
List<Emp> emps = mapper.selectByExampleJoinDateAndJob(example);
emps.forEach(o -> {
System.out.println(o+"\n\t"+o.getBeanDept()+"\n\t"+o.getBeanJob());
});
}
// 任意表为主表 进行连接查询
@Test
public void anyOfFirstTableTest() {
DeptMapper mapper = MyBatisUtil.getSession().getMapper(DeptMapper.class);
// 配置约束容器
DeptExample example = new DeptExample();
DeptExample.Criteria criteria = example.createCriteria();
criteria.andDnameLike("%m%");
List<Dept> depts = mapper.DeptAndJobByExample(example);
depts.forEach( o -> {
System.out.println(o);
o.getJobs().forEach(job -> {
System.out.println("\t"+job);
});
});
}
}
# MyBatis延迟加载
延迟加载在调用的时候加载的查询数据
配置
<configuration>
....
<settings>
<!-- 日志配置 -->
<setting name="logImpl" value="LOG4J"/>
<!-- 打开延迟加载 的开关 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 将积极加载改为消极加载即按需要加载 -->
<setting name="aggressiveLazyLoading" value="false"/>
<!-- 启动二级缓存
<setting name="cacheEnabled" value="true"/>-->
</settings>
....
</configuration>
根据以上应用 Emp 和 Dept 的关系进行应用懒加载
注意:
- Emp实体类中 包含 一方Dept对象
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.sans.mapper.EmpMapper">
<resultMap id="BaseResultMap" type="com.sans.pojo.Emp">
<id column="empno" property="empno" jdbcType="INTEGER"/>
<result column="ename" property="ename" jdbcType="VARCHAR"/>
<result column="job" property="job" jdbcType="VARCHAR"/>
<result column="mgr" property="mgr" jdbcType="INTEGER"/>
<result column="hiredate" property="hiredate" jdbcType="TIMESTAMP"/>
<result column="sal" property="sal" jdbcType="DECIMAL"/>
<result column="comm" property="comm" jdbcType="DECIMAL"/>
<result column="deptno" property="deptno" jdbcType="INTEGER"/>
<result column="jobno" property="jobno" jdbcType="INTEGER"/>
</resultMap>
<!-- ... 省略自动生成的非关键代码 -->
<!-- 懒加载测试 -->
<resultMap id="lazyLoadingMap" type="com.sans.pojo.Emp" extends="BaseResultMap">
<association property="dept" column="deptno" javaType="com.sans.pojo.Dept"
select="com.sans.mapper.DeptMapper.selectByPrimaryKey">
<id column="deptno" property="deptno" javaType="int"/>
<result column="dname" property="dname" javaType="String"/>
<result column="loc" property="loc" javaType="String"/>
</association>
</resultMap>
<select id="lazyLoadingSelectAll" resultMap="lazyLoadingMap">
select * from emp
</select>
</mapper>
以下是测试懒加载的效果实例
无懒加载
DEBUG [main] - ==> Preparing: select * from emp
DEBUG [main] - ==> Parameters:
DEBUG [main] - ====> Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ====> Parameters: 20(Integer)
DEBUG [main] - <==== Total: 1
DEBUG [main] - ====> Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ====> Parameters: 30(Integer)
DEBUG [main] - <==== Total: 1
DEBUG [main] - ====> Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ====> Parameters: 47(Integer)
DEBUG [main] - <==== Total: 1
DEBUG [main] - ====> Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ====> Parameters: 10(Integer)
DEBUG [main] - <==== Total: 1
DEBUG [main] - ====> Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ====> Parameters: 13(Integer)
DEBUG [main] - <==== Total: 1
DEBUG [main] - ====> Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ====> Parameters: 14(Integer)
DEBUG [main] - <==== Total: 1
DEBUG [main] - ====> Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ====> Parameters: 141(Integer)
DEBUG [main] - <==== Total: 1
DEBUG [main] - ====> Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ====> Parameters: 140(Integer)
DEBUG [main] - <==== Total: 1
DEBUG [main] - ====> Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ====> Parameters: 164(Integer)
DEBUG [main] - <==== Total: 1
DEBUG [main] - ====> Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ====> Parameters: 167(Integer)
DEBUG [main] - <==== Total: 1
DEBUG [main] - ====> Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ====> Parameters: 1(Integer)
DEBUG [main] - <==== Total: 1
DEBUG [main] - <== Total: 43
张小三 ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
张小 ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
李四 ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
旺财2 ==> Dept{deptno=47, dname='学术部', loc='B区'} => 学术部
zhansan ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
zhansan2 ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
wangwu ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
Jason ==> Dept{deptno=10, dname='市场部', loc='NEW YORK'} => 市场部
王五 ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
旺财 ==> Dept{deptno=47, dname='学术部', loc='B区'} => 学术部
王小二 ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
kkkkk ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
j1 ==> Dept{deptno=13, dname='bb', loc='b'} => bb
j3 ==> Dept{deptno=14, dname='cc', loc='b'} => cc
j4 ==> Dept{deptno=141, dname='cqc', loc='b'} => cqc
j5 ==> Dept{deptno=140, dname='cqc', loc='b'} => cqc
j6 ==> Dept{deptno=141, dname='cqc', loc='b'} => cqc
hhh ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
king2 ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
mm ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
旺 ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
李 ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
null ==> Dept{deptno=10, dname='市场部', loc='NEW YORK'} => 市场部
jjj ==> Dept{deptno=164, dname='m1', loc='m1'} => m1
mmm ==> Dept{deptno=164, dname='m1', loc='m1'} => m1
jjj2 ==> Dept{deptno=167, dname='m2', loc='m1'} => m2
mmm2 ==> Dept{deptno=167, dname='m2', loc='m1'} => m2
aaa ==> Dept{deptno=47, dname='学术部', loc='B区'} => 学术部
SMITH ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
ALLEN ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
WARD ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
JONES ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
MARTIN ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
BLAKE ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
CLARK ==> Dept{deptno=10, dname='市场部', loc='NEW YORK'} => 市场部
SCOTT ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
KING ==> Dept{deptno=10, dname='市场部', loc='NEW YORK'} => 市场部
TURNER ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
ADAMS ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
JAMES ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
FORD ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
MILLER ==> Dept{deptno=10, dname='市场部', loc='NEW YORK'} => 市场部
赵六 ==> Dept{deptno=1, dname='会计部', loc='海珠区'} => 会计部
懒加载
DEBUG [main] - ==> Preparing: select * from emp
DEBUG [main] - ==> Parameters:
DEBUG [main] - <== Total: 43
DEBUG [main] - ==> Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ==> Parameters: 20(Integer)
DEBUG [main] - <== Total: 1
张小三 ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
张小 ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
DEBUG [main] - ==> Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ==> Parameters: 30(Integer)
DEBUG [main] - <== Total: 1
李四 ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
DEBUG [main] - ==> Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ==> Parameters: 47(Integer)
DEBUG [main] - <== Total: 1
旺财2 ==> Dept{deptno=47, dname='学术部', loc='B区'} => 学术部
zhansan ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
zhansan2 ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
wangwu ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
DEBUG [main] - ==> Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ==> Parameters: 10(Integer)
DEBUG [main] - <== Total: 1
Jason ==> Dept{deptno=10, dname='市场部', loc='NEW YORK'} => 市场部
王五 ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
旺财 ==> Dept{deptno=47, dname='学术部', loc='B区'} => 学术部
王小二 ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
kkkkk ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
DEBUG [main] - ==> Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ==> Parameters: 13(Integer)
DEBUG [main] - <== Total: 1
j1 ==> Dept{deptno=13, dname='bb', loc='b'} => bb
DEBUG [main] - ==> Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ==> Parameters: 14(Integer)
DEBUG [main] - <== Total: 1
j3 ==> Dept{deptno=14, dname='cc', loc='b'} => cc
DEBUG [main] - ==> Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ==> Parameters: 141(Integer)
DEBUG [main] - <== Total: 1
j4 ==> Dept{deptno=141, dname='cqc', loc='b'} => cqc
DEBUG [main] - ==> Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ==> Parameters: 140(Integer)
DEBUG [main] - <== Total: 1
j5 ==> Dept{deptno=140, dname='cqc', loc='b'} => cqc
j6 ==> Dept{deptno=141, dname='cqc', loc='b'} => cqc
hhh ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
king2 ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
mm ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
旺 ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
李 ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
null ==> Dept{deptno=10, dname='市场部', loc='NEW YORK'} => 市场部
DEBUG [main] - ==> Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ==> Parameters: 164(Integer)
DEBUG [main] - <== Total: 1
jjj ==> Dept{deptno=164, dname='m1', loc='m1'} => m1
mmm ==> Dept{deptno=164, dname='m1', loc='m1'} => m1
DEBUG [main] - ==> Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ==> Parameters: 167(Integer)
DEBUG [main] - <== Total: 1
jjj2 ==> Dept{deptno=167, dname='m2', loc='m1'} => m2
mmm2 ==> Dept{deptno=167, dname='m2', loc='m1'} => m2
aaa ==> Dept{deptno=47, dname='学术部', loc='B区'} => 学术部
SMITH ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
ALLEN ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
WARD ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
JONES ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
MARTIN ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
BLAKE ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
CLARK ==> Dept{deptno=10, dname='市场部', loc='NEW YORK'} => 市场部
SCOTT ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
KING ==> Dept{deptno=10, dname='市场部', loc='NEW YORK'} => 市场部
TURNER ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
ADAMS ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
JAMES ==> Dept{deptno=30, dname='SALES', loc='CHICAGO'} => SALES
FORD ==> Dept{deptno=20, dname='RESEARCH', loc='DALLAS'} => RESEARCH
MILLER ==> Dept{deptno=10, dname='市场部', loc='NEW YORK'} => 市场部
DEBUG [main] - ==> Preparing: select deptno, dname, loc from dept where deptno = ?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
赵六 ==> Dept{deptno=1, dname='会计部', loc='海珠区'} => 会计部
# MyBatis缓存
MyBatis缓存是一般的ORM 框架都会提供的功能,目的就是提升查询的效率和减少数据库的压力。
将经常查询的数据存在缓存(内存)中,用户查询该数据的时候不需要从库上 查询,而是直接从缓存中查询,提高查询效率,解决高并发问题。 MyBatis 也有一级缓存 和 二级缓存,并且预留了集成 第三方缓存的接口
# 一级缓存
Mybatis自带的 缓存 ,在构造sqlSession对象时内部有个 HashMap 结构存储缓存数据,它的作用域范围是 sqlSession 。如果两次查询用同一个sqlSession进行查询语句,则第一次会通过数据库获取到数据库 ,二次会缓存中获取数据!
缓存清除条件:
- session.clearCache()
- session.close()
- 执行 增删改
- 事务回滚
- 事务提交
应用实例
import com.pojo.Team;
import com.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
public class CacheTest {
private SqlSession sqlSession = MybatisUtil.getSqlSession();
@Test
public void test01() {
Team t1 = sqlSession.selectOne("com.mapper.TeamMapper.queryById",1003);
System.out.println("t1 : " + t1);
Team t2 = sqlSession.selectOne("com.mapper.TeamMapper.queryById",1003);
System.out.println("t2 : " + t2);
MybatisUtil.close();
//换 sqlSession ,刷新缓存
sqlSession = MybatisUtil.getSqlSession();
Team t3 = sqlSession.selectOne("com.mapper.TeamMapper.queryById",1003);
System.out.println("t3 : " + t3);
int num = sqlSession.delete("com.mapper.TeamMapper.delById",1000);
sqlSession.commit();
System.out.println("num : " + num);
Team t4 = sqlSession.selectOne("com.mapper.TeamMapper.queryById",1003);
sqlSession.close();
}
}
/* 运行结果
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1f760b47]
DEBUG [main] - ==> Preparing: select * from team where teamId=?
DEBUG [main] - ==> Parameters: 1003(Integer)
DEBUG [main] - <== Total: 1
t1 : Team{teamId=1003, teamName='老八', location='温州', createTime=Wed Feb 17 00:00:00 CST 2021}
t2 : Team{teamId=1003, teamName='老八', location='温州', createTime=Wed Feb 17 00:00:00 CST 2021}
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1f760b47]
DEBUG [main] - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1f760b47]
DEBUG [main] - Returned connection 527829831 to pool.
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Checked out connection 527829831 from pool.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1f760b47]
DEBUG [main] - ==> Preparing: select * from team where teamId=?
DEBUG [main] - ==> Parameters: 1003(Integer)
DEBUG [main] - <== Total: 1
t3 : Team{teamId=1003, teamName='老八', location='温州', createTime=Wed Feb 17 00:00:00 CST 2021}
DEBUG [main] - ==> Preparing: DELETE FROM `mybatis`.`team` WHERE `teamId` = ?
DEBUG [main] - ==> Parameters: 1000(Integer)
DEBUG [main] - <== Updates: 0
DEBUG [main] - Committing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1f760b47]
num : 0
DEBUG [main] - ==> Preparing: select * from team where teamId=?
DEBUG [main] - ==> Parameters: 1003(Integer)
DEBUG [main] - <== Total: 1
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1f760b47]
DEBUG [main] - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1f760b47]
DEBUG [main] - Returned connection 527829831 to pool.
*/
# 二级缓存
MyBatis 二级缓存是全局缓存,作用域超出 SqlSession 范围之外,其作用域是 mapper 的同一命名空间!
两个不同的sqlSession在同一 命名空间 下,执行的sql语句参数相同 ,最终执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据,从而提高性能!
应用实例
修改配置文件 ==mybatis.xml==
···
<settings>
<!-- 日志配置-->
<setting name="logImpl" value="LOG4J"/>
<!-- 是否启动二级缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>
···
修改映射文件 ==TeamMapper.xml==
<mapper namespace="com.mapper.TeamMapper">
<!-- 二级缓存标签-->
<cache></cache>
····
<mapper/>
实体类实现 ==Serializable接口==
package com.pojo;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
public class Team implements Serializable {
private Integer teamId;
private String teamName;
private String location;
private Date createTime;
//一对多的体现:一方持有多方的对象
private List<Player> playerList1;
private List<Player> playerList2;
//省略 set、get、toString、构造 方法
}
测试
import com.mapper.TeamMapper;
import com.pojo.Team;
import com.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
public class CacheTest {
@Test
public void test02() {
//查 数据 保留缓存
SqlSession sqlSession1 = MybatisUtil.getSqlSession();
Team t1 = sqlSession1.selectOne("com.mapper.TeamMapper.queryById" , 1019);
System.out.println("t1 : " + t1);
//清空一级缓存,保留二级缓存
MybatisUtil.close();
//测试 是否保留二级缓存
SqlSession sqlSession2 = MybatisUtil.getSqlSession();
Team t2 = sqlSession2.selectOne("com.mapper.TeamMapper.queryById" , 1019);
System.out.println("t2 : " + t2);
MybatisUtil.close();
//测试 删除数据 清空二级缓存
SqlSession sqlSession3 = MybatisUtil.getSqlSession();
//删除不存在的数据(假删除)
int i = sqlSession3.delete("com.mapper.TeamMapper.delById" , 10000);
System.out.println("i : " + i);
sqlSession3.commit();
MybatisUtil.close();
//测试 是否保留二级缓存
SqlSession sqlSession4 = MybatisUtil.getSqlSession();
Team t4 = sqlSession4.selectOne("com.mapper.TeamMapper.queryById" , 1019);
System.out.println("t4 : " + t4);
MybatisUtil.close();
}
}
/*运行结果
DEBUG [main] - Created connection 292138977.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1169afe1]
DEBUG [main] - ==> Preparing: select * from team where teamId=?
DEBUG [main] - ==> Parameters: 1019(Integer)
DEBUG [main] - <== Total: 1
t1 : Team{teamId=1019, teamName='哥斯拉', location='华盛顿', createTime=Sun Jul 25 00:00:00 CST 2021}
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1169afe1]
DEBUG [main] - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1169afe1]
DEBUG [main] - Returned connection 292138977 to pool.
WARN [main] - As you are using functionality that deserializes object streams, it is recommended to define the JEP-290 serial filter. Please refer to https://docs.oracle.com/pls/topic/lookup?ctx=javase15&id=GUID-8296D8E8-2B93-4B9A-856E-0A65AF9B8C66
DEBUG [main] - Cache Hit Ratio [com.mapper.TeamMapper]: 0.5
t2 : Team{teamId=1019, teamName='哥斯拉', location='华盛顿', createTime=Sun Jul 25 00:00:00 CST 2021}
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Checked out connection 292138977 from pool.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1169afe1]
DEBUG [main] - ==> Preparing: DELETE FROM `mybatis`.`team` WHERE `teamId` = ?
DEBUG [main] - ==> Parameters: 10000(Integer)
DEBUG [main] - <== Updates: 0
i : 0
DEBUG [main] - Committing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1169afe1]
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1169afe1]
DEBUG [main] - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1169afe1]
DEBUG [main] - Returned connection 292138977 to pool.
DEBUG [main] - Cache Hit Ratio [com.mapper.TeamMapper]: 0.3333333333333333
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Checked out connection 292138977 from pool.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1169afe1]
DEBUG [main] - ==> Preparing: select * from team where teamId=?
DEBUG [main] - ==> Parameters: 1019(Integer)
DEBUG [main] - <== Total: 1
t4 : Team{teamId=1019, teamName='哥斯拉', location='华盛顿', createTime=Sun Jul 25 00:00:00 CST 2021}
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1169afe1]
DEBUG [main] - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@1169afe1]
DEBUG [main] - Returned connection 292138977 to pool.
*/
# 二级缓存其他设置
映射文件中的cache标签
···
<!--二级缓存默认配置-->
<cache>
<property name="eviction" value="LRU"/><!--回收策略为LRU-->
<property name="flushInterval" value="60000"/><!--自动刷新时间间隔为60S-->
<property name="size" value="1024"/><!--最多缓存1024个引用对象-->
<property name="readOnly" value="true"/><!--只读-->
</cache>
···
属性 | 说明 |
---|---|
eviction | 代表的是缓存回收策略,目前 MyBatis 提供以下策略。 LRU:使用较少,移除最长时间不用的对象; FIFO:先进先出,按对象进入缓存的顺序来移除它们; SOFT:软引用,移除基于垃圾回收器状态和软引用规则的对象; WEAK:弱引用,更积极地移除基于垃圾收集器状态和弱引用规则的对象 |
flushInterval | 刷新间隔时间,单位为毫秒,这里配置的是 100 秒刷新,如果省略该配置,那么只有当 SQL 被执行的时候才会刷新缓存 |
size | 引用数目,正整数,代表缓存最多可以存储多少个对象,不宜设置过大。设置过大会导致内存溢出。这里配置的是 1024 个对象 |
readOnly | 只读,默认值为 false,意味着缓存数据只能读取而不能修改,这样设置的好处是可以快速读取缓存,缺点是没有办法修改缓存 |
重用cache标签配置
在命名空间中共享相同的缓存配置和实例,可以使用cache-ref 元素来引用另外一个缓存。引用实例:
···
<!--需要指定 映射文件的位置-->
<cache-ref namespace="com.mapper.TeamMapper" />
···
# 反向工程
Mybatis 提供逆向生成工具,该工具可以根据 数据库中的表 自动生成单表的 pojo 类、mapper 映射文件和 mapper 接口。大大缩减了开发时间!!(应用前提,不同库表名不建议写相同!)
MyBatis反向生成方式有多种,本次引用了:项目生成 和 Maven项目工具生成
# 项目生成
项目到手直接配置 generatorConfig.xml
该文件即可,我们只需要关注以下的配置信息
<jdbcConnection>
:JDBC 库连接- **driverClass:**驱动加载
- **connectionURL:**连接URL
- **userId:**账号
- **password:**密码
<javaModelGenerator>.targetPackage
:指定生成 实体类路径<sqlMapGenerator>.targetPackage
:指定生成 映射文件路径<javaClientGenerator>.targetPackage
:指定生成 mapper接口路径<table>.tableName
:指定 反向生成 的数据库 表
对以上标签详细了解以及配置的信息,可在一下jar包路径了解详细: ==mybatis-generator-core-1.3.2.jar\org\mybatis\generator\config\xml\mybatis-generator-config_1_0.dtd==
依赖包
- log4j-1.2.16.jar(日志
- mybatis-3.5.6.jar(mybatis
- mybatis-generator-core-1.3.2.jar (反向工程
- mysql-connector-java-8.0.16.jar(jdbc
项目生成问题:
- 反向生成项目 不能通过子项目进行应用该项目,否则 反向生成找不到 src 根路径
- 反向生成项目,配置 指定的包名路径 必须与应用的项目一致 (实例和映射的路径
示例:
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.cj.jdbc.Driver"
connectionURL="jdbc:mysql://127.0.0.1:3306/test?
useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT"
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:生成POjO类的位置 -->
<javaModelGenerator targetPackage="com.sans.pojo"
targetProject=".\src">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
<!-- 从数据库返回的值被清理前后的空格 -->
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- targetProject:mapper映射文件生成的位置 -->
<sqlMapGenerator targetPackage="com.sans.mapper"
targetProject=".\src">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</sqlMapGenerator>
<!-- targetPackage:mapper接口生成的位置 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.sans.mapper"
targetProject=".\src">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</javaClientGenerator>
<!-- 指定数据库表 -->
<table tableName="emp">
<!-- 大小写也一起搬 -->
<property name="useActualColumnNames" value="true"/>
</table>
<table tableName="dept">
<property name="useActualColumnNames" value="true"/>
</table>
<!-- 有些表的字段需要指定java类型
<table schema="" tableName="">
<columnOverride column="" javaType="" />
</table> -->
</context>
</generatorConfiguration>
GeneratorSqlmap
类 执行入口
import java.io.File;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;
public class GeneratorSqlmap {
// 指定 逆向工程配置文件
private static String configName = "generatorConfig.xml";
public void generator() throws Exception{
List<String> warnings = new ArrayList<>();
boolean overwrite = true;
// 通过类加载的路径进入查找文件
// 中文路径转义
String decode = URLDecoder.decode(this.getClass().getClassLoader().getResource(configName).getFile() , "utf-8");
System.out.println("decode : " + decode);
File configFile = new File(decode);
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) {
try {
// 直接指定路径可能会有问题
new GeneratorSqlmap().generator();
} catch (Exception e) {
e.printStackTrace();
}
}
}
执行成功会在指定的路径生成出文件
反向工程应用
直接上测试代码,就不多bb
package com.sans;
import com.sans.mapper.DeptMapper;
import com.sans.pojo.Dept;
import com.sans.pojo.DeptExample;
import com.sans.utils.MyBatisUtil;
import org.junit.Test;
import java.util.List;
public class Demo {
/** 反向工程 单表应用测试
* 已 Dept对象 示例
* 反向生成的接口方法
* // 查询所有条数 (带约束
* int countByExample(DeptExample example);
* // 删除指定数据 (带约束
* int deleteByExample(DeptExample example);
* // 删除指定id
* int deleteByPrimaryKey(Integer deptno);
* // 添加数据 (完全字段添加
* int insert(Dept record);
* // 添加数据 (选择添加字段
* int insertSelective(Dept record);
* // 查询 约束
* List<Dept> selectByExample(DeptExample example);
* // 查询 指定id
* Dept selectByPrimaryKey(Integer deptno);
* // 修改 约束 选择性
* int updateByExampleSelective(@Param("record") Dept record, @Param("example") DeptExample example);
* // 修改 约束
* int updateByExample(@Param("record") Dept record, @Param("example") DeptExample example);
* // 修改 指定id 选择性
* int updateByPrimaryKeySelective(Dept record);
* // 修改 指定id
* int updateByPrimaryKey(Dept record);
*
* 以上方法 共同信息: id、约束、选择性
*
* 本次测试围绕:实体约束对象的使用 和 对象属性选择的操作 和 基本的CRUD
*/
DeptMapper mapper = MyBatisUtil.getSession().getMapper(DeptMapper.class);
// 查询 条数
// sql示例:select count(*) from dept
@Test
public void countTest() {
int i = mapper.countByExample(null);
System.out.println("i : " + i);
}
// 查询 普通
// sql示例:select deptno, dname, loc from dept
@Test
public void findTest() {
List<Dept> depts = mapper.selectByExample(null);
depts.forEach(System.out::println);
}
// 查询 约束
// sql示例:select deptno, dname, loc from dept WHERE ( deptno = 44 ) or( loc like '天河' )
@Test
public void findByExampleTest() {
DeptExample example = new DeptExample();
// 约束容器
DeptExample.Criteria criteria = example.createCriteria();
DeptExample.Criteria criteria2 = example.or();
criteria.andDeptnoEqualTo(44);
criteria2.andLocLike("天河");
List<Dept> depts = mapper.selectByExample(example);
depts.forEach(System.out::println);
}
// 添加
// sql示例:insert into dept (deptno, dname, loc ) values (null, '会计部1', '汇总' )
@Test
public void addTest() {
Dept dept = new Dept();
dept.setDname("会计部1");
dept.setLoc("汇总");
int i = mapper.insert(dept);
System.out.println(" ["+i+"] ");
}
// 添加 选择
// sql示例:insert into dept ( loc ) values ( '汇总' )
@Test
public void addSSelectiveTest() {
Dept dept = new Dept();
dept.setLoc("汇总");
int i = mapper.insertSelective(dept);
System.out.println(" ["+i+"] ");
}
// 修改 约束 选择
// sql示例:update dept SET dname = '张三', loc = '张三2' WHERE ( deptno = 50 )
@Test
public void updateTest() {
Dept dept = new Dept();
dept.setDname("张三");
dept.setLoc("张三2");
DeptExample example = new DeptExample();
DeptExample.Criteria criteria = example.createCriteria();
criteria.andDeptnoEqualTo(50);
int i = mapper.updateByExampleSelective(dept , example);
System.out.println(" ["+i+"] ");
}
}
# Maven项目
Maven反向生成 需要在 ==pom.xml== 中配置 ==org.mybatis.generator== 插件工具进行生成
应用实现
库数据引入&展示 (库名mybatis)
CREATE TABLE `team` (
`teamId` int NOT NULL AUTO_INCREMENT COMMENT '球队ID',
`teamName` varchar(50) DEFAULT NULL COMMENT '球队名称',
`location` varchar(50) DEFAULT NULL COMMENT '球队位置',
`createTime` date DEFAULT NULL COMMENT '球队建立时间',
PRIMARY KEY (`teamId`)
) ENGINE=InnoDB AUTO_INCREMENT=1026 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `player` (
`playerId` int NOT NULL,
`playerName` varchar(100) DEFAULT NULL,
`playerNum` int DEFAULT NULL,
`teamId` int DEFAULT NULL,
PRIMARY KEY (`playerId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `gamerecord` (
`recordId` varchar(36) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`homeTeamId` int DEFAULT NULL COMMENT ' 主队id',
`gameDate` datetime DEFAULT NULL COMMENT '比赛时间',
`score` int DEFAULT NULL COMMENT '得分',
`visitingTeamId` int DEFAULT NULL COMMENT '客队id',
PRIMARY KEY (`recordId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
配置文件 ==pom.xml==
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
·····
<dependencies>
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!--jdbc-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version>
</dependency>
<!--测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--mybatis日志 依赖-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--xml解析 依赖-->
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-classworlds</artifactId>
<version>2.5.2</version>
</dependency>
<!--xml解析 依赖-->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.5</version>
</dependency>
</dependencies>
<build>
<plugins>
<!--反向生成插件-->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.5</version>
<configuration>
<!--配置文件的路径-->
<configurationFile>src/main/resources/generatorConfig.xml</configurationFile>
<overwrite>true</overwrite>
</configuration>
<dependencies>
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.5</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>
创建逆向生成配置文件 ==resources/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>
<!--1、数据库驱动jar:添加自己的jar路径 -->
<classPathEntry
location="D:\Maven\repository\mysql\mysql-connector-java\8.0.23\mysql-connector-java-8.0.23.jar" />
<context id="MyBatis" targetRuntime="MyBatis3">
<!--去除注释 -->
<commentGenerator>
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!--2、数据库连接 -->
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT"
userId="root"
password="root">
</jdbcConnection>
<!--
默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer;
为 true时把JDBC DECIMAL和NUMERIC类型解析为java.math.BigDecimal
false: Integer
true: BigDecimal (双精度浮点型变量double可以处理16位有效数)
-->
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!--3、生成实体类 指定包名 以及生成的地址 (可以自定义地址,但是路径不存在不会自动创建
使用Maven生成在target目录下,会自动创建) -->
<javaModelGenerator targetPackage="com.pojo"
targetProject="src\main\java">
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!--4、生成SQLmapper.xml映射文件 -->
<sqlMapGenerator targetPackage="com.mapper"
targetProject="src\main\resources">
</sqlMapGenerator>
<!--5、生成Dao(Mapper)接口文件,-->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.mapper"
targetProject="src\main\java">
</javaClientGenerator>
<!--6、要生成哪些表(更改tableName和domainObjectName就可以) -->
<!-- tableName:要生成的表名
enableCountByExample:Count语句中加入where条件查询,默认为true开启
enableUpdateByExample:Update语句中加入where条件查询,默认为true开启
enableDeleteByExample:Delete语句中加入where条件查询,默认为true开启
enableSelectByExample:Select多条语句中加入where条件查询,默认为true开启
selectByExampleQueryId:Select单个对象语句中加入where条件查询,默认为true开启
-->
<!-- <table tableName="Team"-->
<!-- enableCountByExample="false"-->
<!-- enableUpdateByExample="false"-->
<!-- enableUpdateByPrimaryKey="false"-->
<!-- enableDeleteByExample="false"-->
<!-- enableDeleteByPrimaryKey="false"-->
<!-- enableSelectByExample="false"-->
<!-- selectByExampleQueryId="false">-->
<!-- <property name="useActualColumnNames" value="true"/>-->
<!-- </table>-->
<table tableName="team">
<!--应用 库中对应的名称 (大小写也搬过去)-->
<property name="useActualColumnNames" value="true"/>
</table>
<table tableName="player">
<property name="useActualColumnNames" value="true"/>
</table>
<table tableName="gamerecord">
<property name="useActualColumnNames" value="true"/>
</table>
</context>
</generatorConfiguration>
Maven插件 反向生成 插件命令行:==mybatis-generator:generate==
测试(应用测试主要部分,多余就不赘述了)
import com.mapper.TeamMapper;
import com.pojo.Team;
import com.pojo.TeamExample;
import com.utils.MybatisUtil;
import org.junit.Test;
import java.util.Date;
import java.util.List;
public class TeamTest {
private TeamMapper teamMapper = MybatisUtil.getSqlSession().getMapper(TeamMapper.class);
//查 主键
@Test
public void test01() {
Team team = teamMapper.selectByPrimaryKey(1019);
System.out.println("team : " + team);
}
//查总数 无约束
@Test
public void test02() {
TeamExample example = new TeamExample();
//查总数
long l = teamMapper.countByExample(example);
System.out.println("l : " + l);
}
//添加
@Test
public void test03() {
Team team = new Team();
team.setTeamName("bozhu-test");
int insert = teamMapper.insert(team);
MybatisUtil.getSqlSession().commit();
System.out.println("insert : " + insert);
}
//动态添加
@Test
public void test04() {
Team team = new Team();
team.setTeamName("bozhu-test2");
int insert = teamMapper.insertSelective(team);
MybatisUtil.getSqlSession().commit();
System.out.println("insert : " + insert);
}
//修改 指定key
@Test
public void test05() {
Team team = teamMapper.selectByPrimaryKey(1026);
team.setTeamName("老哥");
team.setLocation("bj");
team.setCreateTime(new Date());
int i = teamMapper.updateByPrimaryKey(team);
MybatisUtil.getSqlSession().commit();
System.out.println("i : " + i);
}
//Example约束 应用
/**
* Example 约束服务
* criteria1容器 约束方法名
* add+约束列+约束方式
* ····
* OR:criteria2容器 约束方法名
* add+约束列+约束方式
* ····
*/
@Test
public void test06() {
//设置多条件 服务
TeamExample example = new TeamExample();
// criteria 多条件 容器
TeamExample.Criteria criteria = example.createCriteria();
// or 添加’或‘运算符 约束
TeamExample.Criteria criteria2 = example.or();
//为容器添加条件
criteria.andTeamIdBetween(1001,1100);
criteria2.andTeamIdBetween(1,3);
List<Team> teamList = teamMapper.selectByExample(example);
teamList.forEach(team -> System.out.println(team));
}
}
# 分页插件
Mybatis分页查询,可直接 通过插件进行 快速实现分页功能!
在配置中添加插件一定一定要遵循标签的顺序 查看顺序
依赖包:
- jsqlparser-0.9.5.jar
- pagehelper-4.2.1.jar
Mybatis全局配置插件
<configuration>
...
<plugins>
<!-- com.github.pagehelper为PageHelper类所在包名 -->
<plugin interceptor="com.github.pagehelper.PageHelper">
<property name="dialect" value="mysql" />
<!-- 3.3.0版本可用 - 分页参数合理化,默认false禁用 -->
<!-- 启用合理化时,如果pageNum<1会查询第一页,如果pageNum>pages会查询最后一页 -->
<!-- 禁用合理化时,如果pageNum<1或pageNum>pages会返回空数据 -->
<property name="reasonable" value="true" />
</plugin>
</plugins>
...
</configuration>
测试
@Test
public void test() {
EmpMapper mapper = MyBatisUtil.getSession().getMapper(EmpMapper.class);
int pageNum = 2;
int pageSize = 5;
// 以下分页代码必须定义在 sql查询前的语句,否则分页查询会失效
PageHelper.startPage(pageNum , pageSize);
List<Emp> emps = mapper.selectByExample(null);
emps.forEach(System.out::println);
PageInfo<Emp> page = new PageInfo<>(emps);
System.out.println("总记录数:" + page.getTotal());
System.out.println("总页数:" + page.getPages());
System.out.println("上页:" + page.getPrePage());
System.out.println("下页:" + page.getNextPage());
}
# MyBatis问题
# 线程优化
ThreadLocal
ThreadLocal是一个线程内部的存储类,可以在指定线程内存储数据,数据存储以后,只有指定线程可以得到存储数据
SqlSession线程是不安全的线程,利用ThreadLocal实现多线程也能独立完成业务,防止SqlSession同时访问并发问题
ThreadLocal可看作集合容器 ,里面存放的都是唯一且都是相同的副本,里面存储的对象都是独立的
ThreadLocal不是一个线程的本地实现版本,也不是一个Thread。ThreadLocal是为每一个使用该变量的线程都提供一个变量值的副本, 是Java中一种较为特殊的线程绑定机制,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突
实现应用
项目结构(在原有的基础上优化)
.
|
├── src
| ├── main
| | ├── java
| | | └── com
| | | ├── dao
| | | | ├── TeamDao
| | | | └── TeamDaoImpl
| | | ├── pojo
| | | | ├── Team
| | | | └── Team.xml
| | | └── utils
| | | └── MybatisUtil
| | └── resources
| | ├── log4j.properties
| | └── mybatis.xml
| test
| └── ...
└── pom.xml
添加映射SQL ==Team.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">
<!--namespace="名称必须与映射的类的名字一致,是完全限定名"-->
<mapper namespace="com.pojo.Team">
<!--
id="自定义名称,id不能重复;相当于dao中的方法名称"
resultType="使用的要求:实体类中的属性名与表中的列名一致"
-->
<!-- 查询所有-->
<select id="findAll" resultType="com.pojo.Team">
select * from team;
</select>
<!-- 查询指定-->
<select id="findId" resultType="com.pojo.Team">
select * from team where teamId=#{teamId}
</select>
<select id="findName" resultType="com.pojo.Team">
select * from team where teamName=#{teamName}
</select>
<!--
parameterType="指定对象作为参数"
#{对象属性名}
-->
<!-- 添加数据-->
<insert id="add" parameterType="com.pojo.Team">
INSERT INTO `mybatis`.`team`(`teamName`, `location`, `createTime`) VALUES (#{teamName}, #{location}, #{createTime})
</insert>
<!-- 修改数据-->
<update id="updateById" parameterType="com.pojo.Team">
UPDATE `team` SET teamName=#{teamName},location=#{location} WHERE teamId=#{teamId}
</update>
<!-- 删除数据-->
<delete id="deleteById" parameterType="com.pojo.Team">
DELETE FROM `mybatis`.`team` WHERE `teamId` = #{id}
</delete>
<delete id="deleteByName" parameterType="com.pojo.Team">
DELETE FROM `mybatis`.`team` WHERE `teamName` = #{name}
</delete>
</mapper>
工具类实现 ==MybatisUtil== (应用主要)
package com.utils;
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;
public class MybatisUtil {
private static SqlSessionFactory sqlSessionFactory;
private static ThreadLocal<SqlSession> sqlSessionThreadLocal = new ThreadLocal<>();
static {
try {
Reader reader = Resources.getResourceAsReader("mybatis.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSession getSqlSession(){
SqlSession sqlSession = sqlSessionThreadLocal.get();
if (sqlSession == null){
sqlSession = sqlSessionFactory.openSession();
sqlSessionThreadLocal.set(sqlSession);
}
return sqlSession;
}
public static void close() {
SqlSession sqlSession = sqlSessionThreadLocal.get();
if (sqlSession != null) {
sqlSession.close();
sqlSessionThreadLocal.remove();
}
}
}
实现dao,创建 ==TeamDaoImpl接口==
package com.dao;
import com.pojo.Team;
import java.util.List;
public interface TeamDaoImpl {
public List<Team> findAll();
public Team queryById(int id);
public Team queryByName(String name);
public int add(Team team);
public int updateById(int id , Team newTeam);
public int updateByName(String name , Team newTeam);
public int delById(int id);
public int delByName(String name);
}
创建 ==TeamDao实现类== (业务实现)
package com.dao;
import com.pojo.Team;
import com.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import java.util.List;
public class TeamDao implements TeamDaoImpl{
@Override
public List<Team> findAll() {
SqlSession sqlSession = MybatisUtil.getSqlSession();
List<Team> teams = sqlSession.selectList("com.pojo.Team.findAll");
sqlSession.commit();
return teams;
}
@Override
public Team queryById(int id) {
SqlSession sqlSession = MybatisUtil.getSqlSession();
return sqlSession.selectOne("com.pojo.Team.queryById" , id);
}
@Override
public Team queryByName(String name) {
SqlSession sqlSession = MybatisUtil.getSqlSession();
return sqlSession.selectOne("com.pojo.Team.queryByName" , name);
}
@Override
public int add(Team team) {
SqlSession sqlSession = MybatisUtil.getSqlSession();
int num = sqlSession.insert("com.pojo.Team.add",team);
sqlSession.commit();
return num;
}
@Override
public int updateById(int id ,Team newTeam) {
SqlSession sqlSession = MybatisUtil.getSqlSession();
Team team = sqlSession.selectOne("com.pojo.Team.queryById" , id);
team.setTeamName(newTeam.getTeamName());
team.setLocation(newTeam.getLocation());
int num = sqlSession.update("com.pojo.Team.updateById" , team);
sqlSession.commit();
return num;
}
@Override
public int updateByName(String name , Team newTeam) {
SqlSession sqlSession = MybatisUtil.getSqlSession();
Team team = sqlSession.selectOne("com.pojo.Team.queryByName" , name);
team.setTeamName(newTeam.getTeamName());
team.setLocation(newTeam.getLocation());
int num = sqlSession.update("com.pojo.Team.updateById" , team);
sqlSession.commit();
return num;
}
@Override
public int delById(int id) {
SqlSession sqlSession = MybatisUtil.getSqlSession();
int num = sqlSession.delete("com.pojo.Team.deleteById" , id);
sqlSession.commit();
return num;
}
@Override
public int delByName(String name) {
SqlSession sqlSession = MybatisUtil.getSqlSession();
int num = sqlSession.delete("com.pojo.Team.deleteByName" , name);
sqlSession.commit();
return num;
}
}
测试类 (前提数据库中要有相应数据)
import com.dao.TeamDao;
import com.dao.TeamDaoImpl;
import com.pojo.Team;
import org.junit.Test;
import java.util.Date;
import java.util.List;
public class DaoApp {
TeamDaoImpl teamDao = new TeamDao();
@Test
public void testFindAll() {
List<Team> all = teamDao.findAll();
all.forEach(team -> System.out.println(team));
}
@Test
public void testQueryById() {
Team team = teamDao.queryById(1009);
System.out.println("team : " + team);
}
@Test
public void testQueryByName() {
Team team = teamDao.queryByName("老八");
System.out.println("team : " + team);
}
@Test
public void testAdd() {
Team team = new Team("火箭","洛杉矶",new Date());
int add = teamDao.add(team);
System.out.println("add : " + add);
}
@Test
public void testUpdateById() {
Team newTeam = new Team("老鸽","上海");
int i = teamDao.updateById(1004 , newTeam);
System.out.println("i : " + i);
}
@Test
public void testUpdateByName() {
Team newTeam = new Team("法外狂徒","南京");
int i = teamDao.updateByName("老鸽" , newTeam);
System.out.println("i : " + i);
}
@Test
public void testDelById() {
int i = teamDao.delById(1004);
System.out.println("i : " + i);
}
@Test
public void testDelByName() {
int num = teamDao.delByName("火箭");
System.out.println("num : " + num);
}
}
# Sql语句字段调配问题
# 库添加获取id问题
数据库 在添加对象 的时候 序列 都是也默认形式在 数据库 中进行自增的 ,并且自增后 后端是获取不到该对象的自增数据,因此可通过以下方式进行 自定义添加并获取
主要涉及点:
- 配置映射 文件 ,
insert标签
中 添加selectKey标签
进行 对指定列 的值 自定义添加并获取- selectKey标签属性说明:
- keyProperty:表示自增的id值 赋值 到哪个实体类的属性
- order:AFTER(之后)、BEFORE(之前)两值,表示在sql语句执行 之前 或 之后
- 一般情况 BEFORE 用于 指定id并获取;AFTER 用于 获取自增后的id值
- resultType:表示返回值类型
实现应用
项目结构(在原有的基础上优化)
.
|
├── src
| ├── main
| | ├── java
| | | └── com
| | | ├── ···
| | | ├── pojo
| | | | └── GameRecord
| | | └── utils
| | | └── MybatisUtil
| | └── resources
| | ├── com
| | | └── mapper
| | | ├──GameRecordMapper.xml
| | | └──TeamMapper.xml
| | ├── log4j.properties
| | └── mybatis.xml
| test
| └── ...
└── pom.xml
数据库添加表(球队比赛信息)
CREATE TABLE `gamerecord` (
`recordId` varchar(36) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`homeTeamId` int DEFAULT NULL COMMENT ' 主队id',
`gameDate` datetime DEFAULT NULL COMMENT '比赛时间',
`score` int DEFAULT NULL COMMENT '得分',
`visitingTeamId` int DEFAULT NULL COMMENT '客队id',
PRIMARY KEY (`recordId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
实体类对象 ==GameRecord==
package com.pojo;
import java.util.Date;
/**
* ClassName: GameRecord
* 球队记录实体类
* @author wanglina
* @version 1.0
*/
public class GameRecord {
private String recordId;
private Integer homeTeamId;
private Date gameDate;
private Integer score;
private Integer visitingTeamId;
//省略 set 和 get 方法
@Override
public String toString() {
return "GameRecord{" +
"recordId='" + recordId + '\'' +
", homeTeamId=" + homeTeamId +
", gameDate=" + gameDate +
", score=" + score +
", visitingTeamId=" + visitingTeamId +
'}';
}
}
创建接口 ==GameRecordMapper==
package com.mapper;
import com.pojo.GameRecord;
public interface GameRecordMapper {
int add(GameRecord gameRecord);
}
添加 ==resources/com/mapper/GameRecordMapper.xml== 配置文件 (order="BEFORE"应用)
<?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">
<!--namespace="名称必须与映射的类的名字一致,是完全限定名"-->
<mapper namespace="com.mapper.GameRecordMapper">
<!--
新增成功后将自增的ID赋值给参数属性 的 TeamId
selectKey标签属性说明:
keyProperty:表示自增的id值 赋值 到哪个实体类的属性
order:AFTER(之后)、BEFORE(之前)两值,表示在sql语句执行 之前 或 之后
一般情况 BEFORE 用于 指定id并获取;AFTER 用于 获取自增后的id值
resultType:表示返回值类型
-->
<insert id="add" parameterType="com.pojo.GameRecord">
<selectKey keyProperty="recordId" order="BEFORE" resultType="java.lang.String">
select uuid();
</selectKey>
INSERT INTO `mybatis`.`gamerecord`(`recordId`, `homeTeamId`, `gameDate`, `score`, `visitingTeamId`)
VALUES (#{recordId}, #{homeTeamId}, #{gameDate}, #{score}, #{visitingTeamId})
</insert>
</mapper>
修改==TeamMapper.xml==文件中的insert标签
<insert id="add" parameterType="com.pojo.Team">
<selectKey keyProperty="teamId" order="AFTER" resultType="java.lang.Integer">
SELECT LAST_INSERT_ID()
</selectKey>
INSERT INTO `mybatis`.`team`(`teamName`, `location`, `createTime`) VALUES (#{teamName}, #{location}, #{createTime})
</insert>
在 ==mybatis.xml配置文件== 中注册映射文件
<!-- 注册映射文件 -->
<mappers>
···
<mapper resource="com/mapper/TeamMapper.xml"/>
<mapper resource="com/mapper/GameRecordMapper.xml"/>
</mappers>
测试:
import com.mapper.GameRecordMapper;
import com.mapper.TeamMapper;
import com.pojo.GameRecord;
import com.pojo.Team;
import com.utils.MybatisUtil;
import org.junit.Test;
import java.util.Date;
//id自增测试
public class id_incrementTest {
private TeamMapper teamMapper = MybatisUtil.getSqlSession().getMapper(TeamMapper.class);
private GameRecordMapper gameRecordMapper = MybatisUtil.getSqlSession().getMapper(GameRecordMapper.class);
@Test
public void teamMapper_order_AFTER() {
Team team = new Team("哥斯拉","东京",new Date());
int add = teamMapper.add(team);
MybatisUtil.getSqlSession().commit();
System.out.println("add : " + add);
System.out.println("team : " + team);
}
@Test
public void gameRecordMapper_order_BEFORE() {
GameRecord gameRecord = new GameRecord();
gameRecord.setHomeTeamId(1002);
gameRecord.setGameDate(new Date());
gameRecord.setVisitingTeamId(1001);
gameRecord.setScore(90);
int add = gameRecordMapper.add(gameRecord);
MybatisUtil.getSqlSession().commit();
System.out.println("add : " + add);
System.out.println("gameRecord : " + gameRecord);
}
}
# 映射问题
# 数据库列名与实体类属性不匹配问题
一般情况 数据库列名 和 实体类属性名 一样,在以上条件为前提下 MyBatis 会自动匹配数据映射问题
解决方案:
- sql 查询的别名 与 实体类属性名 一致
- 通过resultMap自行映射
数据库 表 列名
teamId、teamName、location、createTime
创建 ==Team2实体类==
package com.pojo;
import java.util.Date;
/**
* @author Sans
*/
//用于映射测试
public class Team2 {
private Integer team_id;
private String team_name;
private String location;
private Date createTime;
//set和get 省略
@Override
public String toString() {
return "Team2{" +
"team_id=" + team_id +
", team_name='" + team_name + '\'' +
", location='" + location + '\'' +
", createTime=" + createTime +
'}';
}
}
==TeamMapper接口方法==
public interface TeamMapper {
List<Team2> queryAll3();
List<Team2> queryAll4();
}
修改 ==TeamMapper.xml映射文件== 添加sql语句
<!-- 库列名 与 类属性名 不一致问题-->
<!-- 解决方案1 (别名形式匹配)-->
<select id="queryAll3" resultType="com.pojo.Team2">
select teamId 'team_id',teamName 'team_name',location,createTime from team
</select>
<!-- 解决方案2(自行配置映射)-->
<select id="queryAll4" resultMap="baseMap">
select * from team
</select>
<resultMap id="baseMap" type="com.pojo.Team2">
<id column="teamId" property="team_id" javaType="java.lang.Integer"/>
<result column="teamName" property="team_name" javaType="java.lang.String"/>
<result column="location" property="location" javaType="java.lang.String"/>
<result column="createTime" property="createTime" javaType="java.util.Date"/>
</resultMap>
测试:
//处理自定义类型数据
@Test
public void queryAll3() {
List<Team2> teamList = teamMapper.queryAll3();
teamList.forEach(team -> System.out.println(team));
}
@Test
public void queryAll4() {
List<Team2> teamList = teamMapper.queryAll4();
teamList.forEach(team -> System.out.println(team));
}
# 分页问题
查询 处理出来的大量数据需要分页功能浏览大量数据,缓解查询结果带来的压力
注意:
- SQL语句末尾不能添加 分号
;
应用
添加依赖
<!-- 分页应用-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.1</version>
</dependency>
设置配置文件 ==mybatis.xml== (plugins标签节点 在 environments标签节点 之前)
<!-- 配置分页插件-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"/>
</plugins>
测试(在以往的基础上执行)
private TeamMapper teamMapper = MybatisUtil.getSqlSession().getMapper(TeamMapper.class);
//分页插件应用
@Test
public void pageTest() {
PageHelper.startPage(1,3);
List<Team> all = teamMapper.findAll();
all.forEach(team -> System.out.println(team));
System.out.println("其他分页信息:");
PageInfo<Team> info = new PageInfo<>(all);
System.out.println("当前页:"+info.getPageNum());
System.out.println("总页数:"+info.getPages());
System.out.println("前一页:"+info.getPrePage());
System.out.println("后一页:"+info.getNextPage());
System.out.println("所有导航页号:");
for (int navigatepageNum : info.getNavigatepageNums()) {
System.out.println("\t" + navigatepageNum);
}
}
/* 运行结果
Team{teamId=1, teamName='张三', location='上海', createTime=Thu Jul 15 00:00:00 CST 2021}
Team{teamId=2, teamName='李四', location='深圳', createTime=Wed Jun 02 00:00:00 CST 2021}
Team{teamId=3, teamName='王五', location='南京', createTime=Sun Aug 01 00:00:00 CST 2021}
其他分页信息:
当前页:1
总页数:3
前一页:0
后一页:2
所有导航页号:
1
2
3
*/
# 实用篇
# 动态SQL
实用标签说明 :
主标签
- <select>
- <insert>
- <update>
- <delete>
逻辑标签
- <where>
- <if>
功能标签
- <trim> 管理子标签 (首尾括号 , 逗号分隔 , ...)