文章目录

  • 9. 通用mapper注解开发
    • 9.1. 通过mapper入门案例
      • 9.1.1. 添加mapper依赖
      • 9.1.2. 相关配置文件
        • - jdbc.properties
        • - mybatis-config.xml
        • - log4j.properties
      • 9.13. 集成通用mapper
      • 9.1.4. 添加表和类
        • 品牌表brands
        • 实体类Brands
      • 9.1.5. 接口继承通用mapper
      • 9.1.6. 测试
    • 9.2. ORM
      • 9.2.1. @Table (JPA)
      • 9.2.2. @Id (JPA)
      • 9.2.3. 主键策略
      • 9.2.4. @Column (JPA)
      • 9.2.5. @ColumnType (mapper)
      • 9.2.6. @Transient (JPA)
    • 9.2. 通用mapper
      • 9.2.1. 常用方法
      • 9.2.2. 通用Example
        • 1. 查询
        • 2. 动态SQL
        • 3. 设置查询列
        • 4. 行级锁
      • 9.2.3. 自定义方法(注解方式)
        • 1. 参数传递
        • 2. 返回值接收
        • 3. 多对一查询
        • 4. 一对多查询
      • 9.2.4. 自定义方法(XML方式)

9. 通用mapper注解开发

除了XML配置开发的方式,mybatis还支持注解开发,并且为了提高开发效率,

通用Mapper都可以极大的方便开发人员。可以随意的按照自己的需要选择通用方法,还可以很方便的开发自己的通用方法。

极其方便的使用MyBatis单表的增删改查。

支持单表操作,不支持通用的多表联合查询。

通用 Mapper 支持 Mybatis-3.2.4 及以上版本。

github地址:https://gitee.com/free/Mapper

码云官方文档:https://gitee.com/free/Mapper/wikis/Home

9.1. 通过mapper入门案例

使用通用mapper完成数据库的读写操作

  1. 添加通用mapper的依赖包
  2. 配置mybatis-config.xml
  3. 集成通用mapper功能
  4. 准备表、类数据读写
  5. 编写mapper接口
  6. 测试:使用通用mapper读写数据
9.1.1. 添加mapper依赖
<!-- https://mvnrepository.com/artifact/tk.mybatis/mapper -->
<dependency><groupId>tk.mybatis</groupId><artifactId>mapper</artifactId><version>4.1.5</version>
</dependency>

其他依赖

<dependencies>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.4.5</version>
</dependency><!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.18</version>
</dependency><!-- junit测试 -->
<dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13</version><scope>test</scope>
</dependency><!-- log4j日志依赖 -->
<dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version>
</dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.10</version><scope>test</scope>
</dependency></dependencies><build><resources><!-- 打包src/main/java 下面的属性文件和xml文件--><!-- 在进行模块化开放打jar包时,maven会将非Java文件过滤掉,xml,properties配置文件等,但是这些文件又是必需的,使用此配置可以在打包时将不会过滤这些必需的配置文件--><resource><directory>src/main/java</directory><includes><include>**/*.properties</include><include>**/*.xml</include></includes><!--是否替换资源中的属性--><filtering>false</filtering></resource></resources></build>
9.1.2. 相关配置文件
- jdbc.properties
//#注意mybatis版本>=6.0使用如下驱动,如果<6.0使用com.mysql.jdbc.Driver
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8&&serverTimezone=Hongkong&useSSL=false
username=root
password=root
- mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><!--properties:作用引入属性配置,这里引入数据库的连接属性配置--><properties resource="jdbc.properties"></properties><!--typeAliases配置别名--><typeAliases><!--扫描包下所有类,取别名,别名默认是类名的小写字母--><package name="com.czxy.mybatis.model"/></typeAliases><environments default="development"><environment id="development"><!--transactionManager:配置事务管理器在 MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施MANAGED – 这个配置几乎没做什么--><transactionManager type="JDBC"/><!--dataSource: 配置数据源type可选值:UNPOOLED 不使用连接池POOLED使用连接池JNDI 从配置好的JNDI数据源获取连接池--><dataSource type="POOLED"><property name="driver" value="${driver}"/><property name="url" value="${url}"/><property name="username" value="${username}"/><property name="password" value="${password}"/></dataSource></environment></environments><!--映射器--><mappers><!--扫描接口--><package name="com.czxy.mybatis.mapper"/></mappers>
</configuration>
- log4j.properties
### 设置###
# debug 日志级别,常用的4个日志级别:ERROR、WARN、 INFO、DEBUG
log4j.rootLogger = debug,stdout,D,E### 输出信息到控制抬 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n### 输出DEBUG 级别以上的日志到=E://logs/error.log ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = E://logs/log.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n### 输出ERROR 级别以上的日志到=E://logs/error.log ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =E://logs/error.log
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n
9.13. 集成通用mapper
  • MyBatisMapperUtils
package com.czxy.mybatis.util;import java.io.IOException;
import java.io.Reader;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 tk.mybatis.mapper.entity.Config;
import tk.mybatis.mapper.mapperhelper.MapperHelper;public class MyBatisMapperUtils {private static SqlSessionFactory sqlSessionFactory = null;/** 使用ThreadLocal为每一个线程独立管理一个session对象 每一个线程只有且仅有单独且唯一的一个session对象* 使用ThreadLocal对session进行管理,可以保证线程安全,避免多实例同时调用同一个session对象* 每一个线程都会独立管理自己的session对象*/private static ThreadLocal<SqlSession> threadlocal = new ThreadLocal<SqlSession>();// 创建sessionFactory对象,因为整个应用程序只需要一个实例工厂// 而静态代码块随着类的加载而执行,而且只执行一次,因此放入静态代码块中初始化工厂实例static {try {//读取配置文件Reader reader = Resources.getResourceAsReader("mybatis-config.xml");//创建会话工厂sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);//关闭读取流reader.close();} catch (IOException e) {e.printStackTrace();}}/*** 新建session会话,并把session放在线程变量中*/private static void newSession() {//从刚刚创建的 sqlSessionFactory 中获取 sessionSqlSession session = sqlSessionFactory.openSession();//创建一个MapperHelperMapperHelper mapperHelper = new MapperHelper();//配置 mapperHelper 后,对Configuration进行加工mapperHelper.processConfiguration(session.getConfiguration());// 将session会话保存在本线程变量中threadlocal.set(session);}/*** 返回session对象* @return session*/public static SqlSession getSession(){//优先从线程变量中取session对象SqlSession session = threadlocal.get();//如果线程变量中的session为null,if(session==null){//新建session会话,并把session放在线程变量中newSession();//再次从线程变量中取session对象session = threadlocal.get();}return session;}/*** 关闭session对象,并从线程变量中删除*/public static void closeSession(){//读取出线程变量中session对象SqlSession session = threadlocal.get();//如果session对象不为空,关闭sessoin对象,并清空线程变量if(session!=null){session.close();threadlocal.set(null);}}/*** 提交并关闭资源*/public static void commitAndclose() {//获取连接SqlSession openSession = getSession();//非空判断if(openSession!=null) {//提交openSession.commit();//关闭资源openSession.close();//从threadlocal中移除sessionthreadlocal.remove();}}
}

实现原理

这种配置方式是通过重写原 Configuration 中的 addMappedStatement 方法来实现的:

@Override
public void addMappedStatement(MappedStatement ms) {try {super.addMappedStatement(ms);//没有任何配置时,使用默认配置if (this.mapperHelper == null) {this.mapperHelper = new MapperHelper();}this.mapperHelper.processMappedStatement(ms);} catch (IllegalArgumentException e) {//这里的异常是导致 Spring 启动死循环的关键位置,为了避免后续会吞异常,这里直接输出e.printStackTrace();throw new RuntimeException(e);}
}
9.1.4. 添加表和类
品牌表brands
DROP TABLE IF EXISTS `brands`;
CREATE TABLE `brands` (`brand_id` INT NOT NULL AUTO_INCREMENT COMMENT '品牌ID',`brand_name` VARCHAR(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT 'none' COMMENT '品牌名称',`brand_img` VARCHAR(150) DEFAULT NULL COMMENT '品牌图标',`create_time` DATETIME DEFAULT NULL COMMENT '创建时间',`data_flag` TINYINT DEFAULT NULL COMMENT '删除标志 -1:删除 1:有效',PRIMARY KEY (`brand_id`)
) ENGINE=INNODB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8insert  into `brands`(`brand_id`,`brand_name`,`brand_img`,`create_time`,`data_flag`) values (1,'瑞士莲','/img/lindt_chocolate.img','2020-01-18 16:32:17',-1),(2,'德芙','/img/dove.img','2020-03-18 09:12:57',1),(3,'费列罗','/img/FERRERO_ROCHER.img','2020-07-06 15:36:19',1),(4,'怡口莲','/img/CADBURY.img','2020-07-13 18:32:51',-1),(5,'百草味','/img/BCW.img','2020-07-20 09:53:26',1),(6,'百草之味','/img/xfj.img','2020-05-22 11:32:17',1);
实体类Brands

这里给productId加上了@ID注解,表示这个属性对应表中的主键列,具体原因后续在介绍注解时会详细介绍

public class Brands {@Idprivate Integer brandId;    // 品牌IDprivate String brandName;   // 品牌名称private String brandImg;    // 品牌图标private Date createTime;    // 创建时间private Character dataFlag; //'删除标志 -1:删除 1:有效',//get set toString()...
}
9.1.5. 接口继承通用mapper
//继承通用Mapper接口
//Mapper中的泛型用于绑定查询结果对应的实体类型
public interface BrandsMapper extends Mapper<Brands> {}
9.1.6. 测试
/*** 测试通用mapper*/
@Test
public void hello(){//1. 获取session连接SqlSession session = MyBatisMapperUtils.getSession();//2. 获取mapper接口BrandsMapper mapper = session.getMapper(BrandsMapper.class);//3. 调用通用mapper的方法Brands brand = mapper.selectByPrimaryKey(1);System.out.println(brand);//关闭连接MyBatisMapperUtils.closeSession();
}

9.2. ORM

  • 通用mapper的自动关系映射

    通用 Mapper 中,默认情况下是将实体类字段按照驼峰转下划线形式的表名列名进行转换

    例如

    实体类的 userName 可以映射到表的 user_name 上。

因此我们在mybatis-config.xml中并未配置开启驼峰命名规则转换,通用mapper已经开启了。

如果数据库中的字段名和实体类的字段名是完全相同的,这中情况下实体和表可以直接映射。

  • 手动绑定映射关系

    通用 Mapper 使用 JPA 注解和自己提供的注解来实现对象关系映射.

    JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML,用以描述对象和关系表的映射关系,并将运行期的实体对象持久化到数据库中。

9.2.1. @Table (JPA)

配置类和表直接的关系,如果表名和类名一致或者符合驼峰命名规则则可以省略不写

//将 User 实体映射到 sys_user 表。
@Table(name = "sys_user")
public class User
9.2.2. @Id (JPA)

@Id 注解和映射无关,它是一个特殊的标记,用于标识数据库中的主键字段。

正常情况下,一个实体类中至少需要一个标记 @Id 注解的字段,存在联合主键时可以标记多个。

如果表中没有主键,类中就可以不标记。

当类中没有存在标记 @Id 注解的字段时,你可以理解为类中的所有字段是联合主键。使用所有的 ByPrimaryKey 相关的方法时,有 where 条件的地方,会将所有列作为条件。

配置示例:

@Id
private Integer id;

或者联合主键:

@Id
private String phone;
@Id
private Integer productID;
9.2.3. 主键策略

主键策略就是用来获取生成的主键ID

获取主键的注解

  • @KeySql (mapper)

    这是通用 Mapper 的自定义注解,改注解的目的就是替换 @GeneratedValue 注解。推荐使用keysql

  • @GeneratedValue(JPA)

    主键策略注解,用于配置如何生成主键。

新增的@KeySql 注解用于替换 @GeneratedValue 注解,因此 @KeySql 能以更简单方式实现原来的功能,下面的示例都先使用 @KeySql 进行配置,然后在使用 @GeneratedValue,大家可以自己选择。

需要数据库支持自增,其次数据库提供的 JDBC 支持 getGeneratedKeys 方法。

常见的如 MySql,SqlServer 支持这种模式。

  • 用法如下:
@Id
@KeySql(useGeneratedKeys = true)
private Long id;

​ 或:

@Id
@GeneratedValue(generator = "JDBC")
private Long id;
  • 为了让大家容易理解这里配置和 MyBatis 写法的关系,大家可以看看对应生成的 XML 代码:
<!--
useGeneratedKeys:是否使用JDBC方式获取主键,使用getGeneratedKeys 方法获取主键
keyProperty:主键对应属性
-->
<insert id="insert" useGeneratedKeys="true" keyProperty="id">insert into country (id, countryname, countrycode)values (#{id},#{countryname},#{countrycode})
</insert>
9.2.4. @Column (JPA)

配置映射的列名,如果列名和属性名一致或者符合驼峰命名规则则可以省略不写

@Column(name = "user_name")
private String name;

在使用关键字的情况,还会有下面的用法:

@Column(name = "`order`")
private String order;
9.2.5. @ColumnType (mapper)

这个注解提供的 column属性和 @Column 中的 name 作用相同。但是 @Column 的优先级更高。

除了 name 属性外,这个注解主要提供了 jdbcType 属性和 typeHandler 属性。

jdbcType 用于设置特殊数据库类型时指定数据库中的 jdbcType

typeHandler 用于设置特殊类型处理器,常见的是枚举。

用法示例如下:

@ColumnType(column = "countryname",jdbcType = JdbcType.VARCHAR,typeHandler = StringTypeHandler.class)
private String  countryname;
9.2.6. @Transient (JPA)

一般情况下,实体中的字段和数据库表中的字段是一一对应的,但是也有很多情况我们会在实体中增加一些额外的属性,这种情况下,就需要使用 @Transient 注解来告诉通用 Mapper 这不是表中的字段。否则通用mapper会自动映射表中的列,因为表中没有这个列,会抛出异常

@Transient
private String otherThings; //非数据库表中字段

9.2. 通用mapper

9.2.1. 常用方法
  • 查询方法
方法名 描述
T selectOne(T t) 根据实体中的属性进行查询,只能有一个返回值,有多个结果是抛出异常,查询条件使用等号
List select(T t) 根据实体中的属性值进行查询,查询条件使用等号
List selectAll() 查询全部结果
int selectCount(T t) 根据实体中的属性查询总数,查询条件使用等号
T selectByPrimaryKey(Object key) 根据主键字段进行查询
boolean existsWhithPrimaryKey(Object key) 根据主键字段查询记录是否存在
List selectByExample(Object example) 根据Example条件进行查询
T selectOneByExample(Object example) 根据Example条件进行查询,只能有一个返回值
int selectCountByExample(Object example) 根据Example条件进行查询记录数
  • 插入方法
方法名 描述
int insert(T t) 保存一个实体,null的属性也会保存,不会使用数据库默认值
int intsertSelective(T t) 保存一个实体,null的属性不会保存,使用数据库默认值
  • 更新方法
方法名 描述
int updateByPrimaryKey(T t) 根据主键更新实体全部字段,null值会被更新
int updateByPrimaryKeySelective(T t) 根据主键更新实体中不为null值的字段
  • 删除方法
方法名 描述
int delete(T t) 根据实体属性作为条件进行删除,查询条件使用等号
int deletePrimaryKey(Object key) 根据主键字段进行删除
int deleteByExample(Object example) 根据Example条件删除数据
9.2.2. 通用Example

这是由通用 Mapper 提供的一个类,需要自己设置属性名。这个类提供了很多条件查询的方法。

可以帮我们完成sql语句中where条件句的书写,相当于where后面的部分,我们可以根据不同的条件来查询

1. 查询

需求:查询ID > 1 并且 小于等于 3 或者ID 大于5的品牌,结果按照品牌ID降序排序

/*** Example查询*/
@Test
public void exampleSelect(){//1. 获取session连接SqlSession session = MyBatisMapperUtils.getSession();//2. 获取mapper接口BrandMapper mapper = session.getMapper(BrandMapper.class);//创建通用Example//example用于设置关键字条件 or and order byExample example = new Example(Brands.class);//创建criteria,用于设置属性条件: 属性 > < between likeExample.Criteria criteria = example.createCriteria();criteria.andGreaterThan("brandId",1).andLessThanOrEqualTo("brandId",3);//example.or() 另起一个条件example.or().andGreaterThan("brandId",5);//根据ID降序排序example.orderBy("brandId").desc();//使用example查询List<Brands> brands = mapper.selectByExample(example);//关闭链接MyBatisMapperUtils.closeSession();
}

打印的日志:

==>  Preparing: SELECT brand_id,brand_name,brand_img FROM brands WHERE ( ( brand_id > ? and brand_id <= ? ) or ( brand_id > ? ) ) order by brand_id DESC==> Parameters: 1(Integer), 3(Integer), 5(Integer)<==      Total: 3
2. 动态SQL

​ 案例:用户输入的查询条件数量不确定,需要判断参数是否为空,进行动态SQL拼接

需求:根据品牌名称模糊查询,根据创建日期进行范围查询

​ 示例:

/*** Example查询* 根据品牌名称模糊查询,根据创建日期进行范围查询*/
@Test
public void dynamicSQL() throws ParseException {//1. 获取session连接SqlSession session = MyBatisMapperUtils.getSession();//2. 获取mapper接口BrandMapper mapper = session.getMapper(BrandMapper.class);//3. 模拟用户输入的参数Brands brands = new Brands();//品牌名称brands.setBrandName("%百草%");SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");Date startTime = format.parse("2020-05-01");Date endTime = format.parse("2020-11-01");//创建的开始时间brands.setCreateStartTime(startTime);//创建的结束时间brands.setCreateEndTime(endTime);//使用Example查询Example example = new Example(Brands.class);Example.Criteria criteria = example.createCriteria();//动态拼接SQLif(brands.getBrandName()!= null && !brands.getBrandName().equals("")){//根据品牌名称模糊查询criteria.andLike("brandName",brands.getBrandName());}//开始日期if(brands.getCreateStartTime() != null){criteria.andGreaterThanOrEqualTo("createTime",brands.getCreateStartTime());}//结束日期if (brands.getCreateEndTime() != null){criteria.andLessThanOrEqualTo("createTime",brands.getCreateEndTime());}List<Brands> brandsList = mapper.selectByExample(example);//关闭链接MyBatisMapperUtils.closeSession();
}
3. 设置查询列

查询部分列

需求:查询商品ID、商品名称、删除标志

/*** Example查询* 设置查询列*/
@Test
public void exampleColumn(){//1. 获取session连接SqlSession session = MyBatisMapperUtils.getSession();//2. 获取mapper接口BrandMapper mapper = session.getMapper(BrandMapper.class);//创建通用Example//example用于设置关键字条件 or and order byExample example = new Example(Brands.class);example.selectProperties("brandId","brandName","dataFlag");//使用example查询List<Brands> brands = mapper.selectByExample(example);//关闭链接MyBatisMapperUtils.closeSession();
}
4. 行级锁
/*** Example查询* forupdate 行级锁*/
@Test
public void exampleForupdate(){//1. 获取session连接SqlSession session = MyBatisMapperUtils.getSession();//2. 获取mapper接口BrandMapper mapper = session.getMapper(BrandMapper.class);//创建通用Example//example用于设置关键字条件 or and order byExample example = new Example(Brands.class);//开启行级锁example.setForUpdate(true);//查询条件example.createCriteria().andEqualTo("brandId",6);//使用example查询,查询执行到链接关闭之前,该条数据不会释放锁//其他人修改不了该数据List<Brands> brands = mapper.selectByExample(example);//关闭链接MyBatisMapperUtils.closeSession();
}

for update是一种行级锁,又叫排它锁,一旦用户对某个行施加了行级加锁,则该用户可以查询也可以更新被加锁的数据行,其它用户只能查询但不能更新被加锁的数据行.如果其它用户想更新该表中的数据行,则也必须对该表施加行级锁.即使多个用户对一个表均使用了共享更新,但也不允许两个事务同时对一个表进行更新,真正对表进行更新时,是以独占方式锁表,一直到提交或复原该事务为止。行锁永远是独占方式锁。

只有当出现如下之一的条件,才会释放共享更新锁:

1、执行提交(COMMIT)语句

2、退出数据库

3、程序停止运行

9.2.3. 自定义方法(注解方式)

如果通用mapper或者Example满足不了需求,或者不想用以上2中方式,那么可以自定义方法执行SQL,但是需要注意,在对应的 XML 中,不能出现和继承接口中同名的方法!

使用注解自定义SQL查询,通用mapper不会进行驼峰规则映射,只会将属性和列名全转大写,如果相同则进行关系映射。因此需要在mybatis-config.xml中开启驼峰命名规则转换

<settings><!--开启驼峰规则映射--><setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

接口方法

package com.czxy.mybatis.mapper;import com.czxy.mybatis.model.Brands;
import org.apache.ibatis.annotations.Select;
import tk.mybatis.mapper.common.Mapper;public interface BrandsMapper extends Mapper<Brands> {@Select("SELECT * FROM brands WHERE brand_id = #{bid}")public Brands selectBrandById(Integer brandid);
}

测试:

/*** 自定义注解方法*/
@Test
public void selectBrandById(){//1. 获取session连接SqlSession session = MyBatisMapperUtils.getSession();//2. 获取mapper接口BrandMapper mapper = session.getMapper(BrandMapper.class);Brands brands = mapper.selectBrandById(5);System.out.println(brands);//关闭链接MyBatisMapperUtils.closeSession();
}

到此大家应该发现,使用接口注解方式添加方法,关键就是以下几点:

  1. 编写SQL
  2. 传递参数给SQL语句
  3. SQL语句接收参数
  4. 接收返回值
1. 参数传递
  • 单个参数接收和之前XML方式一样

    • 接口方法直接传参
    • SQL中使用#{变量名} 或者${变量名}接收,名称随意,因为就一个参数,不会取错
  • 多个参数传递

    • 使用多个变量传递参数:@Param(“变量名”) ,指定将参数传递给SQL中的哪个变量
    • 使用Map传递参数:key 必须和变量名一致
    • 使用pojo对象传参
  • 准备商品表和商品类

    商品表

CREATE TABLE `product` (`product_id` VARCHAR(32) NOT NULL COMMENT '商品ID',`product_img` VARCHAR(50) DEFAULT NULL COMMENT '商品图片',`product_name` VARCHAR(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '商品名称',`product_sale` INT DEFAULT NULL COMMENT '商品销量',`stock` INT DEFAULT NULL COMMENT '商品库存',`product_price` DOUBLE DEFAULT NULL COMMENT '商品价格',`brandid` INT DEFAULT NULL COMMENT '品牌ID',PRIMARY KEY (`product_id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8insert  into `product`(`product_id`,`product_img`,`product_name`,`product_sale`,`stock`,`product_price`,`brandid`) values ('p01','/img/dove/213231.img','Dove/德芙丝滑牛奶巧克力252g碗装排块糖果糖巧休闲零食品',1800062,2000000,29.9,2),('p02','/img/dove/352342.img','德芙麦提莎麦丽素麦芽脆夹心巧克力豆520g/桶装休闲零食糖果',209710,300000,79.9,2),('p03','/img/dove/635987.img','Dove/德芙丝滑牛奶巧克力252g*2碗装排块休闲零食品糖果',344410,500000,59.8,2),('p04','/img/rocher/845127.img','费列罗榛果巧克力48粒送女友零食婚庆礼盒3*16条喜糖糖果礼',563315,1000000,139,3),('p05','/img/rocher/345311.img','费列罗拉斐尔椰蓉扁桃仁糖果酥球10粒 休闲零食',19460,25000,79.9,3);

商品类

public class Product {@Idprivate String productId;private String productImg;private String productName;private Integer productSale;private Integer stock;private Double productPrice;private Integer brandid;//get set toString()
}
  • 使用多个变量传递参数

    案例: 查询商品价格高于79,并且销量大于200000的商品

package com.czxy.mybatis.mapper;import com.czxy.mybatis.model.Product;import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import tk.mybatis.mapper.common.Mapper;import java.math.BigDecimal;
import java.util.List;
import java.util.Map;public interface ProductMapper extends Mapper<Product> {/*** 使用多个变量传递参数* 必须使用@Param指定赋值给哪个变量* @param price* @param sale* @return*/@Select("SELECT * FROM product  WHERE product_price > #{productPrice} AND product_sale > #{productSale}")public List<Product> listProductByPriceAndSale(@Param("productPrice") BigDecimal price,@Param("productSale") Integer sale);

测试:

/*** 自定义注解方法* 传递多个参数*/
@Test
public void listProductByPriceAndSale(){//1. 获取session连接SqlSession session = MyBatisMapperUtils.getSession();//2. 获取mapper接口ProductMapper mapper = session.getMapper(ProductMapper.class);//3. 传递多个参数查询List<Product> products = mapper.listProductByPriceAndSale(new BigDecimal(79), 200000);//关闭链接MyBatisMapperUtils.closeSession();
}
  • 使用Map传递参数

    接口方法

/*** 使用Map传递多个参数* map的key必须和变量名一致* @param map* @return*/
@Select("SELECT * FROM product WHERE product_price > #{productPrice} AND product_sale > #{productSale}")
public List<Product> listProductByPriceAndSale_map(Map<String,Object> map);

测试:

/*** 自定义注解方法* 使用map传递多个参数*/
@Test
public void listProductByPriceAndSale_map(){//1. 获取session连接SqlSession session = MyBatisMapperUtils.getSession();//2. 获取mapper接口ProductMapper mapper = session.getMapper(ProductMapper.class);HashMap<String, Object> map = new HashMap<>();//map的key必须和变量名一致map.put("productPrice",79.0);map.put("productSale",200000);List<Product> products = mapper.listProductByPriceAndSale_map(map);//关闭链接MyBatisMapperUtils.closeSession();
}
  • 使用pojo传递多个参数

    pojo中拥有变量名对应的get方法,如果没有会报错,mybatis会调用对应的get方法获取参数

    #{productPrice} 对应 getproductPrice()方法

/*** 使用pojo传递多个参数* pojo中拥有变量名对应的get方法,如果没有会报错* mybatis会调用对应的get方法获取参数* @param product* @return*/
@Select("SELECT * FROM product WHERE product_price > #{productPrice} AND product_sale > #{productSale}")
public List<Product> listProductByPriceAndSale_pojo(Product product);

测试:

/*** 自定义注解方法* 使用pojo类传递多个参数*/
@Test
public void listProductByPriceAndSale_pojo(){//1. 获取session连接SqlSession session = MyBatisMapperUtils.getSession();//2. 获取mapper接口ProductMapper mapper = session.getMapper(ProductMapper.class);HashMap<String, Object> map = new HashMap<>();//使用pojo类对象传递参数Product product = new Product();product.setProductPrice(new BigDecimal(79.0));product.setProductSale(200000);List<Product> products = mapper.listProductByPriceAndSale_pojo(product);//关闭链接MyBatisMapperUtils.closeSession();
}
2. 返回值接收

常用接收SQL执行结果的方式是map 和pojo类

  • map示例:
@Select("SELECT * FROM brands WHERE brand_id = #{bid}")
public Map<String,Object> selectBrandById(Integer brandid);
  • pojo接收

    符合以下3中规则的,mybatis会自动将列的值封装到类属性中

  1. 类属性名(忽略大小写)和列名称相同
  2. 符合驼峰名称规则,并且开启驼峰命名规则转换
  3. 手动设置了@Results绑定属性和列的关系
  • 之前xml配置结果映射的方式
<resultMap id="productMap" type="product"><id property="productId" column="product_id" javaType="int" jdbcType="INTEGER"></id><result property="productName" column="product_name" ></result><result property="productImg" column="product_img" ></result>
</resultMap>
  • 现在使用注解方式
/*** 使用resultMap手动绑定映射关系* @param product* @return*/
@Select("SELECT * FROM product WHERE product_price > #{productPrice} AND product_sale > #{productSale}")
@Results(id = "productMap",value = {//id : 标识这是主键//javaType: 指定属性的类型,类名.class 可以省略不写,mybatis会自动推导//JdbcType: 列类型,使用枚举JdbcType获取,可以省略不写,mybatis会自动推导@Result(property = "productId",column = "product_id",id = true,javaType = String.class,jdbcType = JdbcType.VARCHAR),@Result(property = "productImg",column = "product_img")//其他属性省略... 自己写吧
})
public List<Product> listProductByPriceAndSale_results(Product product);
  • @ResultMap
    @ResultMap的用法。当这段@Results代码需要在多个方法用到时,为了提高代码复用性,我们可以为这个@Results注解设置id,然后使用@ResultMap注解来复用这段代码。
@Select("select * from product")
@ResultMap("productMap")
public List<Product> listProducts();
3. 多对一查询

注解方式使用的是嵌套select查询实现多表查询

需求:根据订单ID查询订单时,关联展示用户名和手机号

  • 环境准备:Orders User表和实体类(mybatis XML多表查询已准备)

  • 基于注解实现多对一查询

  1. 编写SQL
  2. 将SQL修改为注解方式
  3. 通过result添加嵌套查询
  4. 测试

User类

OrderMapper

package com.czxy.mybatis.mapper;import com.czxy.mybatis.model.Orders;
import org.apache.ibatis.annotations.*;
import tk.mybatis.mapper.common.Mapper;
import java.util.List;public interface OrderMapper extends Mapper<Orders> {/*** 多对一查询* 需求:查询用户时,将该用户所有的订单查询出来* @return*/@Select("select * from orders")@Results(id = "ordersMap",value = {@Result(property = "orderId",column = "order_id",id = true),@Result(property = "userId",column = "user_id"),//one: 表示不是集合属性,也就是对一的关系//select: 关联执行的查询方法,全限定类名加方法名@Result(property = "users",column = "user_id",one = @One(select = "com.czxy.mybatis.mapper.UserMapper.selectByPrimaryKey"))})public List<Orders> selectOrderByID();
}
  • 测试
package com.czxy.Test;import com.czxy.mybatis.mapper.OrderMapper;
import com.czxy.mybatis.mapper.UserMapper;
import com.czxy.mybatis.model.Orders;
import com.czxy.mybatis.model.User;
import com.czxy.mybatis.util.MyBatisMapperUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;public class OrderTest {/*** 使用多对一查询* 需求:查询用户时,将该用户所有的订单查询出来*/@Testpublic void listOrders(){//1获取session连接SqlSession session = MyBatisMapperUtils.getSession();//2获取mapper 连接OrderMapper mapper = session.getMapper(OrderMapper.class);//3调用接口List<Orders> orders = mapper.selectOrderByID();for (Orders order : orders) {System.out.println(order);}//4关闭连接MyBatisMapperUtils.closeSession();}
}
4. 一对多查询

需求:查询用户时,将该用户所有的订单查询出来

  1. 编写SQL语句
  2. 将SQL语句改写成注解方式
  3. 编写results,通过嵌套select进行关联查询

订单类

  • OrderMapper
import com.czxy.mybatis.model.Orders;
import org.apache.ibatis.annotations.*;
import tk.mybatis.mapper.common.Mapper;
import java.util.List;public interface OrderMapper extends Mapper<Orders> {/*** 一对多查询* 根据用户id查询订单* **/@Select("select * from orders where user_id=#{uid}")//@ResultMap 复用 ordersMap//@ResultMap("ordersMap")public List<Orders>ListOrdersByUID(Integer uid);
}
  • UserMapper
package com.czxy.mybatis.mapper;import com.czxy.mybatis.model.User;
import org.apache.ibatis.annotations.Many;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import tk.mybatis.mapper.common.Mapper;public interface UserMapper extends Mapper<User> {/**** 使用注解方式实现一对多查询* 一个用户对应多个订单* */@Select("SELECT * FROM USER WHERE uid=#{uid}")@Results(id = "userResultMap",value = {@Result(property = "uid",column = "uid",id = true),@Result(property = "orders",column = "uid",many = @Many(select = "com.czxy.mybatis.mapper.OrderMapper.ListOrdersByUID"))})public User selectOneUser(Integer uid);
}
  • 测试
package com.czxy.Test;import com.czxy.mybatis.mapper.OrderMapper;
import com.czxy.mybatis.mapper.UserMapper;
import com.czxy.mybatis.model.Orders;
import com.czxy.mybatis.model.User;
import com.czxy.mybatis.util.MyBatisMapperUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;public class OrderTest {/*** 使用注解方式实现一对多查询* **/@Testpublic void listUserAndOrders(){//1获取session连接SqlSession session = MyBatisMapperUtils.getSession();//2获取mapper 连接UserMapper mapper = session.getMapper(UserMapper.class);//3调用接口User user = mapper.selectOneUser(6);System.out.println(user);//4关闭连接MyBatisMapperUtils.closeSession();}
}
9.2.4. 自定义方法(XML方式)

使用xml自定义方法,要求xml文件和接口同名同包

  • UserMapper.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必须是接口全限定名-->
<mapper namespace="com.czxy.mybatis.mapper.UserMapper"><!--使用xml式实现自定义查询--><select id="selectUserByID" resultType="user">select * from user where uid=#{uid}</select>
</mapper>
  • UserMapper
public interface UserMapper extends Mapper<User> {/*** 使用xml式实现自定义查询* */public User selectUserByID(Integer uid);
}
  • 测试
public class OrderTest {/*** 使用xml式实现自定义查询* **/@Testpublic void selectByXML(){//1获取session连接SqlSession session = MyBatisMapperUtils.getSession();//2获取mapper 连接UserMapper mapper = session.getMapper(UserMapper.class);//3调用接口User user = mapper.selectUserByID(6);System.out.println(user);//4关闭连接MyBatisMapperUtils.closeSession();}
}

MyBatis_通用mapper注解开发相关推荐

  1. 通用Mapper注解方式实现动态SQL

    1.通用Mapper注解方式实现动态SQL 注意:使用动态SQL后,不能使用分页助手,需要手动写分页SQL 对应的mapper接口实现SelectMapper接口 在对应的mapper接口中的方法上

  2. 通用mapper_通用Mapper快速开发,搭建项目

    搭建环境 配置maven依赖的架包 tk.mybatismapper4.0.0-beta3junitjunit4.9log4jlog4j1.2.17cglibcglib2.2org.aspectjas ...

  3. MyBatis复习(十):注解开发

    MyBatis有两种开发方式:XML开发和注解开发 注解与XML对应表格 注解 对应XML 说明 @Insert <insert> 新增SQL @Update <update> ...

  4. 如何开发自己的通用Mapper

    前言 自从发了通用Mapper-0.1.0版本后,我觉得对少数人来说,这可能是他们正好需要的一个工具.至少目前的通用DAO中,很少能有比这个更强大的. 但是对另一部分人来说,使用Mybatis代码生成 ...

  5. @mapper注解的使用_SpringBoot-Mybatis通用mapper使用

    mybatis是一个很好用的工具,但是编写mapper是一件很麻烦的事,自mybatis 3.0开始可以使用注解的方式,极大的简化了xml的编写量,本地想看看mybatis源码,自己扩展写一个工具,在 ...

  6. Mybatis注解开发出现Type interface Mapper.StudentMapper is not known to the MapperRegistry异常解决办法

    Mybatis注解开发出现Type interface Mapper.StudentMapper is not known to the MapperRegistry异常解决办法 在核心配置文件中,配 ...

  7. 通用 Mapper @KeySql 注解 genId 方法详解

    为了方便使用全局主键(例如:Vesta 是一款通用的ID产生器,互联网俗称统一发号器),通用 Mapper 4.0.2 版本增加了新的控制主键生成的策略. @KeySql 注解增加了下面的方法: /* ...

  8. 02JavaWeb之MyBatis详解、SqlSession执行、mapper代理执行、动态SQL语句、注解开发、resultMap、resultType、多参数传递

    MyBatis MyBatis 是一款优秀的持久层框架,用于简化 JDBC 开发,它封装了JDBC大部分的操作. MyBatis 本是 Apache 的一个开源项目iBatis, 2010年这个项目由 ...

  9. MyBatis 为什么需要通用 Mapper ?

    一.通用 Mapper 的用途 ? 我个人最早用 MyBatis 时,先是完全手写,然后用上了 MyBatis 代码生成器(简称为 MBG),在使用 MBG 过程中,发现一个很麻烦的问题,如果数据库字 ...

最新文章

  1. .net平台性能很不错的轻型ORM类Dapper(转)
  2. ubuntu 命令收集
  3. linux 关闭磁盘检测,linux下检测磁盘状态
  4. DL之CNN:利用自定义DeepConvNet【7+1】算法对mnist数据集训练实现手写数字识别并预测(超过99%)
  5. boost::coroutine2模块实现斐波那契数列的测试程序
  6. 优化器是怎么得到执行计划的?
  7. 北京80后整体亮相《北京作家》
  8. docker php composer 使用_如何使用Docker部署PHP开发环境
  9. linux 多个定时器,timer: 一个Linux下的超级精简的多重定时器:可实现成千上万个定时任务,定时精度可达纳秒级别,且同一时间点可添加不同的任务!适用于云后台服务和嵌入式等各种环境。...
  10. 《基于MFC的OpenGL编程》Part 14 Quadrics
  11. 人工智能AI常见的经典K-means聚类算法原理和工作过程
  12. NYOJ455 - 黑色帽子
  13. DoIP协议:通用DoIP首部否定确认码02和03的区别
  14. 【聚类3】密度聚类+层次聚类
  15. matlab 螺纹,关于螺纹边缘提取及参数测量
  16. Gale-Shapley算法(基于python3.6)
  17. 第五篇:uCOS-IInbsp;信号量及其…
  18. 18春计算机应用基础在线答案,南开18春《计算机应用基础》在线作业答案.doc
  19. JavaWeb项目----实现用户登录、注册、对商品信息进行的添加、删除、修改功能
  20. 接口测试平台代码实现48: 自动异常测试-1

热门文章

  1. phantomjs入门使用
  2. 解析Find My iPhone不能工作的原因
  3. 9种最常用数据分析方法,解决90%分析难题
  4. 一文入门DNS?从访问GitHub开始
  5. 点石互动--Zac 之:英文SEO论坛和博客推荐
  6. Springboot实现文件上传下载的简单demo(可参考)
  7. android应用安装包大小优化
  8. 迪米特法则原理(最少知道原则)
  9. holowiews安装及使用案例
  10. IO模型之完全懵逼到似懂非懂