一、注解
前面熟悉了MyBatis的基本使用,针对一些简单的sql语句,如对一张表的增删改查,可以使用MyBatis提供的注解,省去写映射文件的步骤,复杂的多表查询,还是建议使用映射文件的方式
1. Select注解
使用Select注解可以实现数据库查询操作
实现根据部门编号查询部门
实体类:
代码语言:javascript复制@Data
@AllArgsConstructor
@NoArgsConstructor
public class Dept implements Serializable {
/**
* 部门编号
*/
private Integer deptno;
/**
* 部门名称
*/
private String dname;
/**
* 地址
*/
private String loc;
}
定义接口方法:
代码语言:javascript复制public interface DeptMapper {
/**
* 根据部门编号查询部门
* @param deptno
* @return
*/
@Select("select * from dept where deptno = #{deptno}")
Dept findDeptByDeptno(int deptno);
}
SQL语句中使用参数的方式和映射文件中相同,#{}的方式,也可以使用注解指定参数的名称
代码语言:javascript复制public interface DeptMapper {
/**
* 根据部门编号查询部门
* @param deptno
* @return
*/
@Select("select * from dept where deptno = #{no}")
Dept findDeptByDeptno(@Param("no") int deptno);
}
测试方法:
代码语言:javascript复制 //根据部门编号查询部门
@Test
public void test1() {
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
Dept dept = mapper.findDeptByDeptno(10);
System.out.println(dept);
}
2. Insert注解
实现新增一个部门信息
定义接口方法:
代码语言:javascript复制 /**
* 新增一个部门信息
*
* @param dept
* @return
*/
@Insert("insert into dept values(#{deptno},#{dname},#{loc})")
int addDept(Dept dept);
sql参数使用属性名
测试方法:
代码语言:javascript复制 //新增一个部门信息
@Test
public void test2() {
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
Dept dept = new Dept(50,"ALGORITHM","Paris");
int rows = mapper.addDept(dept);
System.out.println(rows);
sqlSession.commit();
}
3. Update注解
实现根据部门编号更新部门信息
定义接口方法:
代码语言:javascript复制 /**
* 根据部门编号更新部门信息
* @param dept
* @return
*/
@Update("update dept set dname = #{dname}, loc = #{loc} where deptno = #{deptno}")
int updateDept(Dept dept);
测试方法:
代码语言:javascript复制 //根据部门编号更新部门信息
@Test
public void test3() {
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
Dept dept = new Dept(50,"ALGORITHM","Tokyo");
int rows = mapper.updateDept(dept);
System.out.println(rows);
sqlSession.commit();
}
4. Delete注解
实现根据部门编号删除部门
定义接口方法:
代码语言:javascript复制 /**
* 根据部门编号删除部门
* @param deptno
* @return
*/
@Delete("delete from dept where deptno = #{deptno}")
int deleteDeptbyDeptno(int deptno);
测试方法:
代码语言:javascript复制 //根据部门编号删除部门
@Test
public void test4() {
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
int rows = mapper.deleteDeptbyDeptno(50);
System.out.println(rows);
sqlSession.commit();
}
二、缓存配置
MyBatis自带一级缓存和二级缓存,还支持第三方缓存,如redis、ehcache
1. 一级缓存
一级缓存默认开启,存在于每个SqlSession对象中,对于完全相同的查询,如果一级缓存中存在,那么不会走数据库,直接返回
针对test1方法,通过sqlSession获取两个Mapper,并执行相同操作,最后看查询结果是否为同一对象:
代码语言:javascript复制 //根据部门编号查询部门
@Test
public void test1() {
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
Dept dept = mapper.findDeptByDeptno(10);
System.out.println(dept);
DeptMapper mapper2 = sqlSession.getMapper(DeptMapper.class);
Dept dept2 = mapper2.findDeptByDeptno(10);
System.out.println(dept2);
System.out.println(dept == dept2);
}
结果:
2. 二级缓存
二级缓存可以跨SqlSession,条件是由同一个SqlSessionFactory创建,二级缓存默认关闭,开启它需要以下步骤
- 2.1 全局开关
在MyBatis核心配置文件中配置,支持二级缓存
代码语言:javascript复制 <settings>
...
<!--开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>
- 2.2 在mapper中开启二级缓存
- java中开启方式: 在接口上使用CacheNamespace注解
@CacheNamespace
public interface DeptMapper {
- 映射文件中开启方式: 使用cache标签,并为需要开启缓存的查询设置useCache="true"
<mapper namespace="com.aruba.mapper.DeptMapper">
<cache/>
<!-- Dept findDeptByDeptno(@Param("no") int deptno);-->
<select id="findDeptByDeptno" resultType="dept" useCache="true">
select * from dept where deptno = #{no}
</select>
</mapper>
另外实体类需要实现序列化接口,因为有可能存储在磁盘中
- 2.3 SqlSession的commit或close操作
SqlSession会首先去二级缓存中查找,如果不存在,就查询数据库,commit()或者close()的时候将数据放入到二级缓存
测试方法:
代码语言:javascript复制public class Test1 {
private SqlSession sqlSession;
private SqlSession sqlSession2;
@Before
public void init() throws IOException {
SqlSessionFactoryBuilder sb = new SqlSessionFactoryBuilder();
// 将配置文件作为参数传入
SqlSessionFactory sqlSessionFactory = sb.build(Resources.getResourceAsStream("sqlMapConfig.xml"));
sqlSession = sqlSessionFactory.openSession();
sqlSession2 = sqlSessionFactory.openSession();
}
//根据部门编号查询部门 -- 二级缓存
@Test
public void test5() {
DeptMapper mapper = sqlSession.getMapper(DeptMapper.class);
Dept dept = mapper.findDeptByDeptno(10);
System.out.println(dept);
sqlSession.commit();
DeptMapper mapper2 = sqlSession2.getMapper(DeptMapper.class);
Dept dept2 = mapper2.findDeptByDeptno(10);
System.out.println(dept2);
}
@After
public void release() {
if (sqlSession != null)
sqlSession.close();
if (sqlSession2 != null)
sqlSession2.close();
}
}
结果:
只执行了一条sql
3. 第三方缓存
涉及分布式系统时,MyBatis自带的缓存就不能满足需求了,下面使用Ehcache作为第三方缓存
导入依赖:
代码语言:javascript复制 <dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.10.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<version>1.7.2</version>
</dependency>
在resources目录下创建配置文件ehcache.xml:
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd"
updateCheck="true" monitoring="autodetect"
dynamicConfig="true">
<diskStore path="D:arubaehcache" />
<defaultCache
maxElementsInMemory="1000"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>
二级缓存配置:
- Java中: CacheNamespace注解,指定使用缓存的Class
@CacheNamespace(implementation = EhcacheCache.class)
public interface DeptMapper {
- 映射文件中: cache标签,指定缓存的全类名
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
三、逆向工程
MyBatis官方提供了一个逆向工程,利用工具自动生成表对应的实体类、映射文件以及接口,并自动实现了一些基本的增删改查
1. 导入依赖:
代码语言:javascript复制 <!-- 代码生成工具jar -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.2</version>
</dependency>
2. 在resources目录下新建generatorConfig.xml:
代码语言:javascript复制<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="testTables" targetRuntime="MyBatis3">
<commentGenerator>
<!-- 是否去除自动生成的注释 true:是 : false:否 -->
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!--数据库连接的信息:驱动类、连接地址、用户名、密码 -->
<!-- <jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mybatis" userId="root"
password="123">
</jdbcConnection> -->
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://127.0.0.1:3306/mydb?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true"
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类的位置 -->
<javaModelGenerator targetPackage="com.aruba.pojo"
targetProject=".reverseMyBatissrcmainjava">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
<!-- 从数据库返回的值被清理前后的空格 -->
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- targetProject:mapper映射文件生成的位置 -->
<sqlMapGenerator targetPackage="com.aruba.mapper"
targetProject=".reverseMyBatissrcmainjava">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</sqlMapGenerator>
<!-- targetPackage:mapper接口生成的位置 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.aruba.mapper"
targetProject=".reverseMyBatissrcmainjava">
<!-- enableSubPackages:是否让schema作为包的后缀 -->
<property name="enableSubPackages" value="false" />
</javaClientGenerator>
<!-- 指定生成的数据库表 -->
<table tableName="dept" domainObjectName="Dept"
enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false"
enableSelectByExample="false" selectByExampleQueryId="false" >
<columnOverride column="id" javaType="Integer" />
</table>
</context>
</generatorConfiguration>
3. 运行下面代码:
需要手动修改下,编译后target目录下generatorConfig.xml的绝对路径
代码语言:javascript复制public class GeneratorSqlmap {
public void generator() throws Exception {
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
// 需要手动修改
File configFile = new File("F:\idelworkspace\mybatis_study\reverseMyBatis\target\classes\generatorConfig.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config,
callback, warnings);
myBatisGenerator.generate(null);
}
public static void main(String[] args) throws Exception {
try {
GeneratorSqlmap generatorSqlmap = new GeneratorSqlmap();
generatorSqlmap.generator();
} catch (Exception e) {
e.printStackTrace();
}
}
}
生成结果:
自带了一些实现:
代码语言:javascript复制public interface DeptMapper {
int deleteByPrimaryKey(Integer deptno);
int insert(Dept record);
int insertSelective(Dept record);
Dept selectByPrimaryKey(Integer deptno);
int updateByPrimaryKeySelective(Dept record);
int updateByPrimaryKey(Dept record);
}
项目地址:
https://gitee.com/aruba/mybatis_study.git