MyBatis学习笔记——4
- 一、MyBatis的高级映射及延迟加载
- 1.1、多对一
- 1.1.1、第一种方式:级联属性映射
- 1.1.2、第二种方式:association
- 1.1.3、第三种方式:分步查询
- 1.2、一对多
- 1.2.1、第一种方式:collection
- 1.2.1、第二种方式:分步查询
- 二、MyBatis的缓存
- 2.1、一级缓存
- 2.2、二级缓存
- 2.3、MyBatis集成EhCache
- 三、MyBatis的逆向工程
- 3.1、 逆向工程配置与生成
- 3.2、测试逆向工程生成的是否好用
- 四、MyBatis使用PageHelper
- 4.1、limit分页
- 4.2、PageHelper插件
- 五、MyBatis的注解式开发
- 5.1、 @Insert
- 5.2、@Delete
- 5.3、@Update
- 5.4、@Select
一、MyBatis的高级映射及延迟加载
1.1、多对一
多种方式,常见的包括三种:
- 第一种方式:一条SQL语句,级联属性映射。
- 第二种方式:一条SQL语句,association。
- 第三种方式:两条SQL语句,分步查询。(这种方式常用:优点一是可复用。优点二是支持懒加载。)
1.1.1、第一种方式:级联属性映射
pojo类Student中添加一个属性:Clazz clazz; 表示学生关联的班级对象。
package com.powernode.mybatis.pojo;public class Student {private Integer sid;private String sname;private Clazz clazz;public Clazz getClazz() {return clazz;}public void setClazz(Clazz clazz) {this.clazz = clazz;}@Overridepublic String toString() {return "Student{" +"sid=" + sid +", sname='" + sname + '\'' +", clazz=" + clazz +'}';}public Student() {}public Student(Integer sid, String sname) {this.sid = sid;this.sname = sname;}public Integer getSid() {return sid;}public void setSid(Integer sid) {this.sid = sid;}public String getSname() {return sname;}public void setSname(String sname) {this.sname = sname;}
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.powernode.mybatis.mapper.StudentMapper"><resultMap id="studentResultMap" type="Student"><id property="sid" column="sid"/><result property="sname" column="sname"/><result property="clazz.cid" column="cid"/><result property="clazz.cname" column="cname"/></resultMap><select id="selectBySid" resultMap="studentResultMap">select s.*, c.* from t_student s join t_clazz c on s.cid = c.cid where sid = #{sid}</select></mapper>
package com.powernode.mybatis.test;import com.powernode.mybatis.mapper.StudentMapper;
import com.powernode.mybatis.pojo.Student;
import com.powernode.mybatis.utils.SqlSessionUtil;
import org.junit.Test;public class StudentMapperTest {@Testpublic void testSelectBySid(){StudentMapper mapper = SqlSessionUtil.openSession().getMapper(StudentMapper.class);Student student = mapper.selectBySid(1);System.out.println(student);}
}
1.1.2、第二种方式:association
其他位置都不需要修改,只需要修改resultMap中的配置:association
即可。
<resultMap id="studentResultMap" type="Student"><id property="sid" column="sid"/><result property="sname" column="sname"/><association property="clazz" javaType="Clazz"><id property="cid" column="cid"/><result property="cname" column="cname"/></association>
</resultMap>
association
翻译为:关联。
学生对象关联一个班级对象。
1.1.3、第三种方式:分步查询
其他位置不需要修改,只需要修改以及添加以下三处:
第一处:association
中select
位置填写sqlId
。sqlId=namespace+id
。其中column
属性作为这条子sql语句的条件。
<resultMap id="studentResultMap" type="Student"><id property="sid" column="sid"/><result property="sname" column="sname"/><association property="clazz"select="com.powernode.mybatis.mapper.ClazzMapper.selectByCid"column="cid"/>
</resultMap><select id="selectBySid" resultMap="studentResultMap">select s.* from t_student s where sid = #{sid}
</select>
第二处:在ClazzMapper接口中添加方法
package com.powernode.mybatis.mapper;import com.powernode.mybatis.pojo.Clazz;/*** Clazz映射器接口* @author 老杜* @version 1.0* @since 1.0*/
public interface ClazzMapper {/*** 根据cid获取Clazz信息* @param cid* @return*/Clazz selectByCid(Integer cid);
}
第三处:在ClazzMapper.xml文件中进行配置
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.powernode.mybatis.mapper.ClazzMapper"><select id="selectByCid" resultType="Clazz">select * from t_clazz where cid = #{cid}</select>
</mapper>
分步优点:
- 第一个优点:代码复用性增强。
- 第二个优点:支持延迟加载。【暂时访问不到的数据可以先不查询。提高程序的执行效率。】
- 懒加载作用:
- 提高性能,尽可能地不查,或者说尽可能的少查,来提高性能
- 如何开启懒加载
association
标签中添加fetchType="lazy"
- 注意:默认情况下是没有开启延迟加载的。需要设置:
fetchType="lazy"
- 这种在
association
标签中配置fetchType="lazy"
,是局部的设置,只对当前的association
关联的sql语句起作用。 - 通过
fetchType="eager"
来关闭懒加载
- 如何设置全局的懒加载开关?
- 在mybatis的核心配置文件中添加
lazyLoadingEnabled
属性,设置为true
- 在mybatis的核心配置文件中添加
- 在实际开发中,开启全局的延迟加载机制,对于特殊不需要使用懒加载的通过
fetchType="eager"
来关闭懒加载
1.2、一对多
一对多的实现,通常是在一的一方中有List集合属性。
在Clazz类中添加List<Student> stus;
属性。
public class Clazz {private Integer cid;private String cname;private List<Student> stus;// set get方法// 构造方法// toString方法
}
一对多的实现通常包括两种实现方式:
- 第一种方式:collection
- 第二种方式:分步查询
1.2.1、第一种方式:collection
package com.powernode.mybatis.mapper;import com.powernode.mybatis.pojo.Clazz;
public interface ClazzMapper {/*** 根据cid获取Clazz信息* @param cid* @return*/Clazz selectByCid(Integer cid);/*** 根据班级编号查询班级信息。同时班级中所有的学生信息也要查询。* @param cid* @return*/Clazz selectClazzAndStusByCid(Integer cid);
}
<resultMap id="clazzResultMap" type="Clazz"><id property="cid" column="cid"/><result property="cname" column="cname"/><collection property="stus" ofType="Student"><id property="sid" column="sid"/><result property="sname" column="sname"/></collection>
</resultMap><select id="selectClazzAndStusByCid" resultMap="clazzResultMap">select * from t_clazz c join t_student s on c.cid = s.cid where c.cid = #{cid}
</select>
注意是ofType,表示“集合中的类型”。
package com.powernode.mybatis.test;import com.powernode.mybatis.mapper.ClazzMapper;
import com.powernode.mybatis.pojo.Clazz;
import com.powernode.mybatis.utils.SqlSessionUtil;
import org.junit.Test;public class ClazzMapperTest {@Testpublic void testSelectClazzAndStusByCid() {ClazzMapper mapper = SqlSessionUtil.openSession().getMapper(ClazzMapper.class);Clazz clazz = mapper.selectClazzAndStusByCid(1001);System.out.println(clazz);}
}
1.2.1、第二种方式:分步查询
修改以下三个位置即可:
<resultMap id="clazzResultMap" type="Clazz"><id property="cid" column="cid"/><result property="cname" column="cname"/><!--主要看这里--><collection property="stus"select="com.powernode.mybatis.mapper.StudentMapper.selectByCid"column="cid"/>
</resultMap><!--sql语句也变化了-->
<select id="selectClazzAndStusByCid" resultMap="clazzResultMap">select * from t_clazz c where c.cid = #{cid}
</select>
/**
* 根据班级编号获取所有的学生。
* @param cid
* @return
*/
List<Student> selectByCid(Integer cid);
<select id="selectByCid" resultType="Student">select * from t_student where cid = #{cid}
</select>
一对多延迟加载机制和多对一是一样的。同样是通过两种方式:
- 第一种:fetchType=“lazy”
- 第二种:修改全局的配置setting,lazyLoadingEnabled=true,如果开启全局延迟加载,想让某个sql不使用延迟加载:fetchType=“eager”
二、MyBatis的缓存
缓存:cache
缓存的作用:通过减少IO的方式,来提高程序的执行效率。
mybatis的缓存:将select语句的查询结果放到缓存(内存)当中,下一次还是这条select语句的话,直接从缓存中取,不再查数据库。一方面是减少了IO。另一方面不再执行繁琐的查找算法。效率大大提升。
mybatis缓存包括:
- 一级缓存:将查询到的数据存储到SqlSession中。
- 二级缓存:将查询到的数据存储到SqlSessionFactory中。
- 或者集成其它第三方的缓存:比如EhCache【Java语言开发的】、Memcache【C语言开发的】等。
常见的缓存技术:
+ 字符串常量池
+ 整数型常量池
+ 线程池
+ 连接池
+ …
缓存只针对于DQL语句,也就是说缓存机制只对应select语句。
2.1、一级缓存
一级缓存默认是开启的。不需要做任何配置。
原理:只要使用同一个SqlSession对象执行同一条SQL语句,就会走缓存。
package com.powernode.mybatis.mapper;import com.powernode.mybatis.pojo.Car;public interface CarMapper {/*** 根据id获取Car信息。* @param id* @return*/Car selectById(Long id);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.powernode.mybatis.mapper.CarMapper"><select id="selectById" resultType="Car">select * from t_car where id = #{id}</select></mapper>
**package com.powernode.mybatis.test;import com.powernode.mybatis.mapper.CarMapper;
import com.powernode.mybatis.pojo.Car;
import com.powernode.mybatis.utils.SqlSessionUtil;
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 org.junit.Test;public class CarMapperTest {@Testpublic void testSelectById() throws Exception{// 注意:不能使用我们封装的SqlSessionUtil工具类。SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();SqlSessionFactory sqlSessionFactory = builder.build(Resources.getResourceAsStream("mybatis-config.xml"));SqlSession sqlSession1 = sqlSessionFactory.openSession();CarMapper mapper1 = sqlSession1.getMapper(CarMapper.class);Car car1 = mapper1.selectById(83L);System.out.println(car1);CarMapper mapper2 = sqlSession1.getMapper(CarMapper.class);Car car2 = mapper2.selectById(83L);System.out.println(car2);SqlSession sqlSession2 = sqlSessionFactory.openSession();CarMapper mapper3 = sqlSession2.getMapper(CarMapper.class);Car car3 = mapper3.selectById(83L);System.out.println(car3);CarMapper mapper4 = sqlSession2.getMapper(CarMapper.class);Car car4 = mapper4.selectById(83L);System.out.println(car4);}
}
什么情况下不走缓存?
- 第一种:不同的SqlSession对象。
- 第二种:查询条件变化了。
一级缓存失效情况包括两种:
- 第一种:第一次查询和第二次查询之间,手动清空了一级缓存。
sqlSession.clearCache();
- 第二种:第一次查询和第二次查询之间,执行了增删改操作。【这个增删改和哪张表没有关系,只要有insert delete update操作,一级缓存就失效。】
/**
* 保存账户信息
*/
void insertAccount();
<insert id="insertAccount">insert into t_act values(3, 'act003', 10000)
</insert>
2.2、二级缓存
二级缓存的范围是SqlSessionFactory
。
使用二级缓存需要具备以下几个条件:
<setting name="cacheEnabled" value="true">
全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。默认就是true,无需设置。- 在需要使用二级缓存的SqlMapper.xml文件中添加配置:
<cache />
- 使用二级缓存的实体类对象必须是可序列化的,也就是必须实现
java.io.Serializable
接口 SqlSession
对象关闭或提交之后,一级缓存中的数据才会被写入到二级缓存当中。此时二级缓存才可用。
测试二级缓存:
XxxMapper.xml文件
<cache/>
Xxx类文件
public class Car implements Serializable {
//......
}
测试类
@Test
public void testSelectById2() throws Exception{SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));SqlSession sqlSession1 = sqlSessionFactory.openSession();CarMapper mapper1 = sqlSession1.getMapper(CarMapper.class);Car car1 = mapper1.selectById(83L);System.out.println(car1);// 关键一步sqlSession1.close();SqlSession sqlSession2 = sqlSessionFactory.openSession();CarMapper mapper2 = sqlSession2.getMapper(CarMapper.class);Car car2 = mapper2.selectById(83L);System.out.println(car2);
}
二级缓存的失效:只要两次查询之间出现了增删改操作。二级缓存就会失效。【一级缓存也会失效】
- eviction:指定从缓存中移除某个对象的淘汰算法。默认采用LRU策略。
- LRU:Least Recently Used。最近最少使用。优先淘汰在间隔时间内使用频率最低的对象。(其实还有一种淘汰算法LFU,最不常用。)
- FIFO:First In First Out。一种先进先出的数据缓存器。先进入二级缓存的对象最先被淘汰。
- SOFT:软引用。淘汰软引用指向的对象。具体算法和JVM的垃圾回收算法有关。
- WEAK:弱引用。淘汰弱引用指向的对象。具体算法和JVM的垃圾回收算法有关。
- flushInterval:
- 二级缓存的刷新时间间隔。单位毫秒。如果没有设置。就代表不刷新缓存,只要内存足够大,一直会向二级缓存中缓存数据。除非执行了增删改。
- readOnly:
- true:多条相同的sql语句执行之后返回的对象是共享的同一个。性能好。但是多线程并发可能会存在安全问题。
- false:多条相同的sql语句执行之后返回的对象是副本,调用了clone方法。性能一般。但安全。
- size:
- 设置二级缓存中最多可存储的java对象数量。默认值1024。
2.3、MyBatis集成EhCache
集成EhCache是为了代替mybatis自带的二级缓存。一级缓存是无法替代的。
mybatis对外提供了接口,也可以集成第三方的缓存组件。比如EhCache、Memcache等。都可以。
EhCache是Java写的。Memcache是C语言写的。所以mybatis集成EhCache较为常见,按照以下步骤操作,就可以完成集成:
第一步:引入mybatis整合ehcache的依赖。
<!--mybatis集成ehcache的组件-->
<dependency><groupId>org.mybatis.caches</groupId><artifactId>mybatis-ehcache</artifactId><version>1.2.2</version>
</dependency>
第二步:在类的根路径下新建echcache.xml文件,并提供以下配置信息。
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"updateCheck="false"><!--磁盘存储:将缓存中暂时不使用的对象,转移到硬盘,类似于Windows系统的虚拟内存--><diskStore path="e:/ehcache"/><!--defaultCache:默认的管理策略--><!--eternal:设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断--><!--maxElementsInMemory:在内存中缓存的element的最大数目--><!--overflowToDisk:如果内存中数据超过内存限制,是否要缓存到磁盘上--><!--diskPersistent:是否在磁盘上持久化。指重启jvm后,数据是否有效。默认为false--><!--timeToIdleSeconds:对象空闲时间(单位:秒),指对象在多长时间没有被访问就会失效。只对eternal为false的有效。默认值0,表示一直可以访问--><!--timeToLiveSeconds:对象存活时间(单位:秒),指对象从创建到失效所需要的时间。只对eternal为false的有效。默认值0,表示一直可以访问--><!--memoryStoreEvictionPolicy:缓存的3 种清空策略--><!--FIFO:first in first out (先进先出)--><!--LFU:Less Frequently Used (最少使用).意思是一直以来最少被使用的。缓存的元素有一个hit 属性,hit 值最小的将会被清出缓存--><!--LRU:Least Recently Used(最近最少使用). (ehcache 默认值).缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存--><defaultCache eternal="false" maxElementsInMemory="1000" overflowToDisk="false" diskPersistent="false"timeToIdleSeconds="0" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LRU"/></ehcache>
第三步:修改SqlMapper.xml文件中的标签,添加type属性。
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
第四步:编写测试程序使用。
@Test
public void testSelectById2() throws Exception{SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));SqlSession sqlSession1 = sqlSessionFactory.openSession();CarMapper mapper1 = sqlSession1.getMapper(CarMapper.class);Car car1 = mapper1.selectById(83L);System.out.println(car1);sqlSession1.close();SqlSession sqlSession2 = sqlSessionFactory.openSession();CarMapper mapper2 = sqlSession2.getMapper(CarMapper.class);Car car2 = mapper2.selectById(83L);System.out.println(car2);
}
三、MyBatis的逆向工程
所谓的逆向工程是:根据数据库表逆向生成Java的pojo类,SqlMapper.xml文件,以及Mapper接口类等。
要完成这个工作,需要借助别人写好的逆向工程插件。
思考:使用这个插件的话,需要给这个插件配置哪些信息?
- pojo类名、包名以及生成位置。
- SqlMapper.xml文件名以及生成位置。
- Mapper接口名以及生成位置。
- 连接数据库的信息。
- 指定哪些表参与逆向工程。
- …
3.1、 逆向工程配置与生成
第一步:基础环境准备
新建模块:模块名
打包方式:jar
第二步:在pom中添加逆向工程插件
<!--定制构建过程-->
<build><!--可配置多个插件--><plugins><!--其中的一个插件:mybatis逆向工程插件--><plugin><!--插件的GAV坐标--><groupId>org.mybatis.generator</groupId><artifactId>mybatis-generator-maven-plugin</artifactId><version>1.3.7</version><!--允许覆盖--><configuration><overwrite>true</overwrite></configuration><!--插件的依赖--><dependencies><!--mysql驱动依赖--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.30</version></dependency></dependencies></plugin></plugins>
</build>
第三步:配置generatorConfig.xml
该文件名必须叫做:generatorConfig.xml
该文件必须放在类的根路径下。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfigurationPUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN""http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"><generatorConfiguration><!--targetRuntime有两个值:MyBatis3Simple:生成的是基础版,只有基本的增删改查。MyBatis3:生成的是增强版,除了基本的增删改查之外还有复杂的增删改查。--><context id="DB2Tables" targetRuntime="MyBatis3"><!--防止生成重复代码--><plugin type="org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin"/><commentGenerator><!--是否去掉生成日期--><property name="suppressDate" value="true"/><!--是否去除注释--><property name="suppressAllComments" value="true"/></commentGenerator><!--连接数据库信息--><jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"connectionURL="jdbc:mysql://localhost:3306/powernode"userId="root"password="root"></jdbcConnection><!-- 生成pojo包名和位置 --><javaModelGenerator targetPackage="com.powernode.mybatis.pojo" targetProject="src/main/java"><!--是否开启子包--><property name="enableSubPackages" value="true"/><!--是否去除字段名的前后空白--><property name="trimStrings" value="true"/></javaModelGenerator><!-- 生成SQL映射文件的包名和位置 --><sqlMapGenerator targetPackage="com.powernode.mybatis.mapper" targetProject="src/main/resources"><!--是否开启子包--><property name="enableSubPackages" value="true"/></sqlMapGenerator><!-- 生成Mapper接口的包名和位置 --><javaClientGeneratortype="xmlMapper"targetPackage="com.powernode.mybatis.mapper"targetProject="src/main/java"><property name="enableSubPackages" value="true"/></javaClientGenerator><!-- 表名和对应的实体类名--><table tableName="t_car" domainObjectName="Car"/></context>
</generatorConfiguration>
第四步:运行插件
3.2、测试逆向工程生成的是否好用
第一步:环境准备
- 依赖:mybatis依赖、mysql驱动依赖、junit依赖、logback依赖
- jdbc.properties
- mybatis-config.xml
- logback.xml
第二步:编写测试程序
package com.powernode.mybatis.test;import com.powernode.mybatis.mapper.CarMapper;
import com.powernode.mybatis.pojo.Car;
import com.powernode.mybatis.pojo.CarExample;
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 org.junit.Test;import java.math.BigDecimal;
import java.util.List;public class GeneratorTest {@Testpublic void testGenerator() throws Exception{SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));SqlSession sqlSession = sqlSessionFactory.openSession();CarMapper mapper = sqlSession.getMapper(CarMapper.class);// 增/*Car car = new Car();car.setCarNum("1111");car.setBrand("比亚迪唐");car.setGuidePrice(new BigDecimal(30.0));car.setProduceTime("2010-10-12");car.setCarType("燃油车");int count = mapper.insert(car);System.out.println("插入了几条记录:" + count);*/// 删/*int count = mapper.deleteByPrimaryKey(83L);System.out.println("删除了几条记录:" + count);*/// 改// 根据主键修改/*Car car = new Car();car.setId(89L);car.setGuidePrice(new BigDecimal(20.0));car.setCarType("新能源");int count = mapper.updateByPrimaryKey(car);System.out.println("更新了几条记录:" + count);*/// 根据主键选择性修改/*car = new Car();car.setId(89L);car.setCarNum("3333");car.setBrand("宝马520Li");car.setProduceTime("1999-01-10");count = mapper.updateByPrimaryKeySelective(car);System.out.println("更新了几条记录:" + count);*/// 查一个Car car = mapper.selectByPrimaryKey(89L);System.out.println(car);// 查所有List<Car> cars = mapper.selectByExample(null);cars.forEach(c -> System.out.println(c));// 多条件查询// QBC 风格:Query By Criteria 一种查询方式,比较面向对象,看不到sql语句。CarExample carExample = new CarExample();carExample.createCriteria().andBrandEqualTo("丰田霸道").andGuidePriceGreaterThan(new BigDecimal(60.0));carExample.or().andProduceTimeBetween("2000-10-11", "2022-10-11");mapper.selectByExample(carExample);sqlSession.commit();}
}
QBC 风格:Query By Criteria 一种查询方式,比较面向对象,看不到sql语句。
四、MyBatis使用PageHelper
4.1、limit分页
mysql的limit后面两个数字:
- 第一个数字:
startIndex
(起始下标。下标从0开始。) - 第二个数字:
pageSize
(每页显示的记录条数)
假设已知页码pageNum,还有每页显示的记录条数pageSize,第一个数字可以动态的获取吗?
- startIndex = (pageNum - 1) * pageSize
所以,标准通用的mysql分页SQL:
select *
from tableName ......
limit (pageNum - 1) * pageSize, pageSize
使用mybatis应该怎么做?
package com.powernode.mybatis.mapper;import com.powernode.mybatis.pojo.Car;
import org.apache.ibatis.annotations.Param;import java.util.List;public interface CarMapper {/*** 通过分页的方式获取Car列表* @param startIndex 页码* @param pageSize 每页显示记录条数* @return*/List<Car> selectAllByPage(@Param("startIndex") Integer startIndex, @Param("pageSize") Integer pageSize);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.powernode.mybatis.mapper.CarMapper"><select id="selectAllByPage" resultType="Car">select * from t_car limit #{startIndex},#{pageSize}</select>
</mapper>
package com.powernode.mybatis.test;import com.powernode.mybatis.mapper.CarMapper;
import com.powernode.mybatis.pojo.Car;
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 org.junit.Test;import java.util.List;public class PageTest {@Testpublic void testPage()throws Exception{SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));SqlSession sqlSession = sqlSessionFactory.openSession();CarMapper mapper = sqlSession.getMapper(CarMapper.class);// 页码Integer pageNum = 2;// 每页显示记录条数Integer pageSize = 3;// 起始下标Integer startIndex = (pageNum - 1) * pageSize;List<Car> cars = mapper.selectAllByPage(startIndex, pageSize);cars.forEach(car -> System.out.println(car));sqlSession.commit();sqlSession.close();}
}
获取数据不难,难的是获取分页相关的数据比较难。可以借助mybatis的PageHelper插件。
4.2、PageHelper插件
使用PageHelper插件进行分页,更加的便捷。
第一步:引入依赖
<dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper</artifactId><version>5.3.1</version>
</dependency>
第二步:在mybatis-config.xml文件中配置插件
typeAliases标签下面进行配置:
<plugins><plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
第三步:编写Java代码
List<Car> selectAll();
<select id="selectAll" resultType="Car">select * from t_car
</select>
关键点:
- 在查询语句之前开启分页功能。
- 在查询语句之后封装
PageInfo
对象。(PageInfo
对象将来会存储到request
域当中。在页面上展示。)
@Test
public void testPageHelper() throws Exception{SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));SqlSession sqlSession = sqlSessionFactory.openSession();CarMapper mapper = sqlSession.getMapper(CarMapper.class);// 开启分页PageHelper.startPage(2, 2);// 执行查询语句List<Car> cars = mapper.selectAll();// 获取分页信息对象PageInfo<Car> pageInfo = new PageInfo<>(cars, 5);System.out.println(pageInfo);
}
执行结果:
PageInfo{pageNum=2, pageSize=2, size=2, startRow=3, endRow=4, total=6, pages=3, list=Page{count=true, pageNum=2, pageSize=2, startRow=2, endRow=4, total=6, pages=3, reasonable=false, pageSizeZero=false}[Car{id=86, carNum='1234', brand='丰田霸道', guidePrice=50.5, produceTime='2020-10-11', carType='燃油车'}, Car{id=87, carNum='1234', brand='丰田霸道', guidePrice=50.5, produceTime='2020-10-11', carType='燃油车'}], prePage=1, nextPage=3, isFirstPage=false, isLastPage=false, hasPreviousPage=true, hasNextPage=true, navigatePages=5, navigateFirstPage=1, navigateLastPage=3, navigatepageNums=[1, 2, 3]}
对执行结果进行格式化:
PageInfo{pageNum=2, pageSize=2, size=2, startRow=3, endRow=4, total=6, pages=3, list=Page{count=true, pageNum=2, pageSize=2, startRow=2, endRow=4, total=6, pages=3, reasonable=false, pageSizeZero=false}[Car{id=86, carNum='1234', brand='丰田霸道', guidePrice=50.5, produceTime='2020-10-11', carType='燃油车'}, Car{id=87, carNum='1234', brand='丰田霸道', guidePrice=50.5, produceTime='2020-10-11', carType='燃油车'}], prePage=1, nextPage=3, isFirstPage=false, isLastPage=false, hasPreviousPage=true, hasNextPage=true, navigatePages=5, navigateFirstPage=1, navigateLastPage=3, navigatepageNums=[1, 2, 3]
}
五、MyBatis的注解式开发
mybatis中也提供了注解式开发方式,采用注解可以减少Sql映射文件的配置。
当然,使用注解式开发的话,sql语句是写在java程序中的,这种方式也会给sql语句的维护带来成本。
官方是这么说的:
使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让你本就复杂的 SQL 语句更加混乱不堪。 因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句。
使用注解编写复杂的SQL是这样的:
原则:简单sql可以注解。复杂sql使用xml。
5.1、 @Insert
package com.powernode.mybatis.mapper;import com.powernode.mybatis.pojo.Car;
import org.apache.ibatis.annotations.Insert;public interface CarMapper {@Insert(value="insert into t_car values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})")int insert(Car car);
}
package com.powernode.mybatis.test;import com.powernode.mybatis.mapper.CarMapper;
import com.powernode.mybatis.pojo.Car;
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 org.junit.Test;public class AnnotationTest {@Testpublic void testInsert() throws Exception{SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));SqlSession sqlSession = sqlSessionFactory.openSession();CarMapper mapper = sqlSession.getMapper(CarMapper.class);Car car = new Car(null, "1112", "卡罗拉", 30.0, "2000-10-10", "燃油车");int count = mapper.insert(car);System.out.println("插入了几条记录:" + count);sqlSession.commit();sqlSession.close();}
}
5.2、@Delete
@Delete("delete from t_car where id = #{id}")
int deleteById(Long id);
@Test
public void testDelete() throws Exception{SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));SqlSession sqlSession = sqlSessionFactory.openSession();CarMapper mapper = sqlSession.getMapper(CarMapper.class);mapper.deleteById(89L);sqlSession.commit();sqlSession.close();
}
5.3、@Update
@Update("update t_car set car_num=#{carNum},brand=#{brand},guide_price=#{guidePrice},produce_time=#{produceTime},car_type=#{carType} where id=#{id}")
int update(Car car);
@Test
public void testUpdate() throws Exception{SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));SqlSession sqlSession = sqlSessionFactory.openSession();CarMapper mapper = sqlSession.getMapper(CarMapper.class);Car car = new Car(88L,"1001", "凯美瑞", 30.0,"2000-11-11", "新能源");mapper.update(car);sqlSession.commit();sqlSession.close();
}
5.4、@Select
@Select("select * from t_car where id = #{id}")
@Results({@Result(column = "id", property = "id", id = true),@Result(column = "car_num", property = "carNum"),@Result(column = "brand", property = "brand"),@Result(column = "guide_price", property = "guidePrice"),@Result(column = "produce_time", property = "produceTime"),@Result(column = "car_type", property = "carType")
})
Car selectById(Long id);
@Test
public void testSelectById() throws Exception{SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));SqlSession sqlSession = sqlSessionFactory.openSession();CarMapper carMapper = sqlSession.getMapper(CarMapper.class);Car car = carMapper.selectById(88L);System.out.println(car);
}