十、MyBatis注解开发
- 创建maven工程,引入依赖
- 创建mybatis核心配置文件SqlMapConfig.xml
- 将log4j.properties文件放入resources中,让控制台打印SQL语句。
- 创建实体类
- 创建持久层接口,并在接口方法上定义Sql语句
public interface UserMapper { @Select("select * from user") List<User> findAll(); }
由于注解在方法上方,而方法中就有参数类型和返回值类型,所以使用注解开发不需要定义参数类型和返回值类型
6、在核心配置文件注册持久层接口,由于没有映射文件,所以只能采用注册接口或注册包的方法。
<!--基于注解开发的话,没有映射文件就注册接口--> <mappers> <package name="com.zj.mapper"/> </mappers>
7、测试方法
@Test public void testFindAllUsers(){ List<User> allUsers = userMapper.findAllUsers(); for (User allUser : allUsers) { System.out.println(allUser); } }
10.1 基于注解实现增删改查
@Select("select * from user") List<User> findAllUsers(); @SelectKey(keyColumn = "id",keyProperty = "id",resultType = int.class,before = false, statement = "select LAST_INSERT_ID()") @Insert("insert into user values (default, #{username}, #{sex},#{address})") void addUser(User user); @Delete("delete from user where id = #{id}") void removeUser(int id); @Update("update user set username = #{username}, sex = #{sex}, address = #{address}" + "where id = #{id}") void updateUser(User user); @Select("select * from user where username like #{username}") List<User> findUserLike(String username);
10.2 动态sql
MyBatis注解开发中有两种方式构建动态Sql:
使用脚本标签(了解即可千万别用,又臭又长)
将Sql嵌套在<script>
内即可使用动态Sql标签:
// 根据任意条件查询 @Select("<script>" + " select * from user\n" + " <where>\n" + " <if test=\"username != null and username.length() != 0\">\n" + " username like #{username}\n" + " </if>\n" + " <if test=\"sex != null and sex.length() != 0\">\n" + " and sex = #{sex}\n" + " </if>\n" + " <if test=\"address != null and address.length() != 0\">\n" + " and address = #{address}\n" + " </if>\n" + " </where>" + "</script>") List<User> findByCondition(User user);
在方法中构建动态Sql
在MyBatis中有@SelectProvider
、@UpdateProvider
、@DeleteProvider
、@InsertProvider
注解。当使用这些注解时将不在注解中直接编写SQL,而是调用某个类的方法来生成SQL。
1、创建类构建sql语句
package com.zj.provider; import com.zj.pojo.User; /*构建sql*/ public class UserProvider { //生成根据任意条件查询的sql语句 public String findByConditionSql(User user){ StringBuffer sb = new StringBuffer("SELECT * FROM user WHERE 1=1"); if (user.getUsername() != null && user.getUsername().length() > 0){ sb.append(" and username = #{username}"); } if (user.getSex() != null && user.getSex().length() > 0){ sb.append(" and sex = #{sex}"); } if (user.getAddress() != null && user.getAddress().length() > 0){ sb.append(" and address = #{address}"); } return sb.toString(); } }
2、持久层
/*根据任意给定条件查询用户,使用某个类构建sql*/ @SelectProvider(type = UserProvider.class,method = "findByConditionSql") List<User> findByConditionSql(User user);
3、测试
@Test public void testFindByConditionSql(){ User user = new User(); user.setUsername("顾田然"); List<User> users = userMapper.findByConditionSql(user); for (User user1 : users) { System.out.println(user1); } }
10.3 自定义映射
当POJO属性名与数据库列名不一致时,需要自定义实体类和结果集的映射关系,在MyBatis注解开发中,使用@Results
定义并使用自定义映射,使用@ResultMap
使用自定义映射,用法如下:
/*自定义映射*/ @Results(id = "userMapper",value = { @Result(id = true,property = "id",column = "id"), @Result(property = "username",column = "username"), @Result(property = "sex",column = "sex"), @Result(property = "address",column = "address") }) @Select("select * from user") List<User> findAllUsers();
10.4 基于注解开启二级缓存
1、POJO类实现Serializable接口。
2、在持久层接口上方加注解@CacheNamespace(blocking=true),该接口的所有方法都支持二级缓存。
@CacheNamespace(blocking = true) public interface UserMapper { …… }
10.5 基于注解实现一对一关联查询
在MyBatis的注解开发中对于多表查询只支持分解查询(N+1),不支持连接查询。
1、创建实体类
public class Student { private int sid; private String name; private int age; private String sex; private Classes classes; // 省略getter/setter/toString } public class Classes { private int cid; private String className; private List<Student> students; // 省略getter/setter/toString }
2、持久层
package com.zj.mapper; import com.zj.pojo.Student; import org.apache.ibatis.annotations.One; import org.apache.ibatis.annotations.Result; import org.apache.ibatis.annotations.Results; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.mapping.FetchType; import java.util.List; public interface StudentMapper { //查询全部学生 @Results(id = "StudentMapper" ,value = { @Result(id = true,property = "sid",column = "sid"), @Result(property = "name",column = "name"), @Result(property = "age",column = "age"), @Result(property = "sex",column = "sex"), //调用查询时传入的参数是哪列 @Result(property = "classes",column = "classId", one = @One(select = "com.zj.mapper.ClassesMapper.findClassById", fetchType = FetchType.EAGER) ) }) @Select("select * from Student") List<Student> findAllStudent(); } package com.zj.mapper; import com.zj.pojo.Classes; import org.apache.ibatis.annotations.Select; public interface ClassesMapper { //根据班级cid查询班级信息 @Select("select * from classes where cid = #{cid}") Classes findClassById(int cid);; }
3、测试
@Test public void testStudentMapper() { List<Student> allStudent = studentMapper.findAllStudent(); for (Student student : allStudent) { System.out.println(student); } }
10.6 基于注解实现一对多关联查询
查询班级信息的时候将该班级下的学生一起查出来。
1、持久层
//查询全部班级(一对多一般使用懒加载) @Results(id = "classesMap",value = { @Result(id = true,property = "cid",column = "cid"), @Result(property = "className",column = "className"), @Result(property = "studentList",column = "cid", many = @Many(select="com.zj.mapper.StudentMapper.findStudentByCid", fetchType = FetchType.LAZY)) }) @Select("select * from classes") List<Classes> findAllClasses(); //根据班级id查询学生 @Select("select * from student where classId = #{classId}") Student findStudentByCid(int classId);
2、测试
@Test public void testFindAllClasses(){ List<Classes> allClasses = classesMapper.findAllClasses(); for (Classes allClass : allClasses) { System.out.println(allClass); } }
10.7 注解和映射文件对比
MyBatis中更推荐使用映射文件开发,Spring、SpringBoot更推荐注解方式。具体使用要视项目情况而定。它们的优点对比如下:
映射文件:
- 代码与Sql语句是解耦的,修改时只需修改配置文件,无需修改源码。
- Sql语句集中,利于快速了解和维护项目。
- 级联查询支持连接查询和分解查询两种方式,注解开发只支持分解查询。
注解:
- 配置简单,开发效率高。
- 类型安全,在编译期即可进行校验,不用等到运行时才发现错误。
十一、PageHelper分页插件
开发过程中如果要进行分页查询,需要传入页数和每页条数。返回页面数据,总条数,总页数,当前页面,每页条数等数据。此时使用PageHelper插件可以快速帮助我们获取这些数据。
PageHelper是一款非常好用的开源免费的Mybatis第三方分页插件。使用该插件时,只要传入分页参数,即可自动生成页面对象。我们使用该插件分页查询所有用户:
1、引入依赖
<!-- PageHelper --> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.3.0</version> </dependency>
2、Mybatis配置文件中配置PageHelper插件
<plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"> <!-- 设置数据库类型--> <property name="helperDialect" value="mysql"/> </plugin> </plugins>
3、测试
@Test public void testFindAllUsers(){ //1、设置分页参数 Page<Object> objects = PageHelper.startPage(1, 3); //查询数据库 List<User> allUsers = userMapper.findAllUsers(); //封装查询结果生成页面对象 PageInfo<User> pageInfo = new PageInfo(allUsers); //打印页面数据 List<User> list = pageInfo.getList(); System.out.println("结果集:"+list); System.out.println("总条数:"+pageInfo.getTotal()); System.out.println("总页数:"+pageInfo.getPages()); System.out.println("当前页:"+pageInfo.getPageNum()); System.out.println("每页条数:"+pageInfo.getSize()); }
十二、MyBatis Generator
MyBatis Generator(MBG)是MyBatis官方提供的代码生成器。它可以根据数据库的表结构自动生成POJO类、持久层接口与映射文件,极大减少了代码的编写量,提高开发效率。
MBG可以作为项目引入使用,也可以作为Maven插件使用,其中作为Maven插件使用更加方便快捷。
1、准备数据库表
2、在pom文件中配置MBG插件
<build> <plugins> <plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.4.2</version> <!--MBG配置--> <configuration> <!--配置文件的位置--> <configurationFile>src/main/resources/generatorConfig.xml</configurationFile> <!--运行显示详情--> <verbose>true</verbose> <!--允许文件覆盖--> <overwrite>true</overwrite> </configuration> </plugin> </plugins> </build>
3、编写MBG配置文件
<?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> <!-- jdbc的jar包位置,插件需要连接数据库 --> <classPathEntry location="D:\Java\apache-maven-3.8.3\repositories\mysql\mysql-connector-java\8.0.26\mysql-connector-java-8.0.26.jar"/> <context id="default" targetRuntime="MyBatis3"> <!-- 是否去除自动生成的注释--> <commentGenerator> <property name="suppressAllComments" value="true"/> </commentGenerator> <!--数据库连接参数--> <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/mybatis" userId="root" password="123456"> </jdbcConnection> <!-- 类型处理器,在数据库类型和java类型之间的转换控制--> <javaTypeResolver> <property name="forceBigDecimals" value="false"/> </javaTypeResolver> <!-- targetProject:POJO类路径 targetProject:生成的POJO类的包(需要提前创建包)--> <javaModelGenerator targetProject="src/main/java" targetPackage="com.zj.pojo"> <!-- 是否生成子包 --> <property name="enableSubPackages" value="false"/> <!-- 设置是否在getter方法中,对String类型字段调用trim()方法 --> <property name="trimStrings" value="true"/> </javaModelGenerator> <!-- targetProject:配置文件路径 targetPackage:生成映射文件的位置(要提前创建文件目录) --> <sqlMapGenerator targetProject="src/main/resources" targetPackage="com.zj.mapper"> <!-- 是否生成子包 --> <property name="enableSubPackages" value="false"/> </sqlMapGenerator> <!-- targetPackage:JAVA类路径 targetProject:生成的持久层接口包 --> <javaClientGenerator targetProject="src/main/java" targetPackage="com.zj.mapper" type="XMLMAPPER"> <!-- 是否生成子包 --> <property name="enableSubPackages" value="false"/> </javaClientGenerator> <!-- 数据库表(根据哪些表生成,可以写多个table标签),表名不要和其他库中的表名一样 --> <table tableName="product"></table> </context> </generatorConfiguration>
4、双击运行插件,自动生成POJO,持久层接口,映射文件:
- Product.java:POJO类
- ProductMapper.java:持久层接口
- ProductMapper.xml:映射文件
- ProductExample.java:查询扩展类,该类可以构造复杂的查询条件。
- Criterion:代表一个字段。
- GeneratedCriteria:抽象类,生成查询条件的工具。
- Criteria:GeneratedCriteria的子类,生成查询条件的工具。
12.1 增删改方法
注意在生成插件后还要搭建mybatis,再添加上配置文件。
//添加 @Test public void testAdd(){ Product product = new Product(0,"英特尔(Intel) i7-13700K 13代 酷睿 处理器",2999.0); int insert = productMapper.insert(product); sqlSession.commit(); } //修改 @Test public void testUpdate(){ Product product = new Product(); product.setId(3); product.setProductname("小天才电话手表"); product.setPrice(499.0); int i = productMapper.updateByPrimaryKey(product); sqlSession.commit(); } //删除 @Test public void testDelete(){ int i = productMapper.deleteByPrimaryKey(2); sqlSession.commit(); }
12.2 查询方法
//根据id查询 @Test public void testFindById(){ Product product = productMapper.selectByPrimaryKey(1); System.out.println(product); } //查询全部 @Test public void testFindAll(){ /*除了根据id查询之外其他的查询方法都需要使用查询扩展对象来构建查询条件*/ ProductExample productExample = new ProductExample(); /*查询全部不需要构建查询条件*/ List<Product> products = productMapper.selectByExample(productExample); for (Product product : products) { System.out.println(product); } } //根据名称查询 @Test public void testFindByName(){ /*构建查询条件*/ ProductExample productExample = new ProductExample(); ProductExample.Criteria criteria = productExample.createCriteria(); criteria.andProductnameLike("%华为Mate40%"); /*查询*/ List<Product> products = productMapper.selectByExample(productExample); for (Product product : products) { System.out.println(product); } }
12.3复杂查询
//多条件的and查询 @Test public void testFindAnd(){ /*构建查询条件*/ ProductExample productExample = new ProductExample(); ProductExample.Criteria criteria = productExample.createCriteria(); criteria.andProductnameLike("%华为%"); criteria.andPriceBetween(3000.0,10000.0); List<Product> products = productMapper.selectByExample(productExample); for (Product product : products) { System.out.println(product); } } //多条件的or查询 @Test public void testFindOr(){ /*构建查询条件1*/ ProductExample productExample = new ProductExample(); ProductExample.Criteria criteria1 = productExample.createCriteria(); criteria1.andProductnameLike("%华为%"); /*构建查询条件2*/ ProductExample.Criteria criteria2 = productExample.createCriteria(); criteria2.andPriceBetween(8000.0,10000.0); /*创建or关系(括号中只放第二个)*/ productExample.or(criteria2); /*查询*/ List<Product> products = productMapper.selectByExample(productExample); for (Product product : products) { System.out.println(product); } }