除了XML配置开发的方式,mybatis还支持注解开发,并且为了提高开发效率,
通用Mapper都可以极大的方便开发人员。可以随意的按照自己的需要选择通用方法,还可以很方便的开发自己的通用方法。
极其方便的使用MyBatis单表的增删改查。
支持单表操作,不支持通用的多表联合查询。
通用 Mapper 支持 Mybatis-3.2.4 及以上版本。
github地址:https://gitee.com/free/Mapper
码云官方文档:https://gitee.com/free/Mapper/wikis/Home
使用通用mapper完成数据库的读写操作
<dependency>
<groupId>tk.mybatisgroupId>
<artifactId>mapperartifactId>
<version>4.1.5version>
dependency>
其他依赖
<dependencies>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.4.5version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.18version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.13version>
<scope>testscope>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.10version>
<scope>testscope>
dependency>
dependencies>
<build>
<resources>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>falsefiltering>
resource>
resources>
build>
//#注意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
<configuration>
<properties resource="jdbc.properties">properties>
<typeAliases>
<package name="com.czxy.mybatis.model"/>
typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<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>
### 设置###
# 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
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 中获取 session
SqlSession session = sqlSessionFactory.openSession();
//创建一个MapperHelper
MapperHelper 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中移除session
threadlocal.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);
}
}
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=utf8
insert 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);
这里给productId加上了@ID注解,表示这个属性对应表中的主键列,具体原因后续在介绍注解时会详细介绍
public class Brands {
@Id
private Integer brandId; // 品牌ID
private String brandName; // 品牌名称
private String brandImg; // 品牌图标
private Date createTime; // 创建时间
private Character dataFlag; //'删除标志 -1:删除 1:有效',
//get set toString()...
}
//继承通用Mapper接口
//Mapper中的泛型用于绑定查询结果对应的实体类型
public interface BrandsMapper extends Mapper<Brands> {
}
/**
* 测试通用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();
}
通用mapper的自动关系映射
通用 Mapper 中,默认情况下是将实体类字段按照驼峰转下划线形式的表名列名进行转换
例如
实体类的 userName
可以映射到表的 user_name
上。
因此我们在mybatis-config.xml中并未配置开启驼峰命名规则转换,通用mapper已经开启了。
如果数据库中的字段名和实体类的字段名是完全相同的,这中情况下实体和表可以直接映射。
手动绑定映射关系
通用 Mapper 使用 JPA 注解和自己提供的注解来实现对象关系映射.
JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML,用以描述对象和关系表的映射关系,并将运行期的实体对象持久化到数据库中。
配置类和表直接的关系,如果表名和类名一致或者符合驼峰命名规则则可以省略不写
//将 User 实体映射到 sys_user 表。
@Table(name = "sys_user")
public class User
@Id
注解和映射无关,它是一个特殊的标记,用于标识数据库中的主键字段。
正常情况下,一个实体类中至少需要一个标记 @Id
注解的字段,存在联合主键时可以标记多个。
如果表中没有主键,类中就可以不标记。
当类中没有存在标记 @Id
注解的字段时,你可以理解为类中的所有字段是联合主键。使用所有的 ByPrimaryKey
相关的方法时,有 where
条件的地方,会将所有列作为条件。
配置示例:
@Id
private Integer id;
或者联合主键:
@Id
private String phone;
@Id
private Integer productID;
主键策略就是用来获取生成的主键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;
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
insert into country (id, countryname, countrycode)
values (#{id},#{countryname},#{countrycode})
insert>
配置映射的列名,如果列名和属性名一致或者符合驼峰命名规则则可以省略不写
@Column(name = "user_name")
private String name;
在使用关键字的情况,还会有下面的用法:
@Column(name = "`order`")
private String order;
这个注解提供的 column
属性和 @Column
中的 name
作用相同。但是 @Column
的优先级更高。
除了 name
属性外,这个注解主要提供了 jdbcType
属性和 typeHandler
属性。
jdbcType
用于设置特殊数据库类型时指定数据库中的 jdbcType
。
typeHandler
用于设置特殊类型处理器,常见的是枚举。
用法示例如下:
@ColumnType(
column = "countryname",
jdbcType = JdbcType.VARCHAR,
typeHandler = StringTypeHandler.class)
private String countryname;
一般情况下,实体中的字段和数据库表中的字段是一一对应的,但是也有很多情况我们会在实体中增加一些额外的属性,这种情况下,就需要使用 @Transient
注解来告诉通用 Mapper 这不是表中的字段。否则通用mapper会自动映射表中的列,因为表中没有这个列,会抛出异常
@Transient
private String otherThings; //非数据库表中字段
方法名 | 描述 |
---|---|
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条件删除数据 |
这是由通用 Mapper 提供的一个类,需要自己设置属性名。这个类提供了很多条件查询的方法。
可以帮我们完成sql语句中where条件句的书写,相当于where后面的部分,我们可以根据不同的条件来查询
需求:查询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 by
Example example = new Example(Brands.class);
//创建criteria,用于设置属性条件: 属性 > < between like
Example.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
案例:用户输入的查询条件数量不确定,需要判断参数是否为空,进行动态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();
//动态拼接SQL
if(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();
}
查询部分列
需求:查询商品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 by
Example example = new Example(Brands.class);
example.selectProperties("brandId","brandName","dataFlag");
//使用example查询
List<Brands> brands = mapper.selectByExample(example);
//关闭链接
MyBatisMapperUtils.closeSession();
}
/**
* 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 by
Example 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、程序停止运行
如果通用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();
}
到此大家应该发现,使用接口注解方式添加方法,关键就是以下几点:
单个参数接收和之前XML方式一样
多个参数传递
准备商品表和商品类
商品表
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=utf8
insert 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 {
@Id
private 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();
}
常用接收SQL执行结果的方式是map 和pojo类
@Select("SELECT * FROM brands WHERE brand_id = #{bid}")
public Map<String,Object> selectBrandById(Integer brandid);
pojo接收
符合以下3中规则的,mybatis会自动将列的值封装到类属性中
<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);
@Select("select * from product")
@ResultMap("productMap")
public List<Product> listProducts();
注解方式使用的是嵌套select查询实现多表查询
需求:根据订单ID查询订单时,关联展示用户名和手机号
环境准备:Orders User表和实体类(mybatis XML多表查询已准备)
基于注解实现多对一查询
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 {
/**
* 使用多对一查询
* 需求:查询用户时,将该用户所有的订单查询出来
*/
@Test
public 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();
}
}
需求:查询用户时,将该用户所有的订单查询出来
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);
}
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 {
/**
* 使用注解方式实现一对多查询
* **/
@Test
public 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();
}
}
<mapper namespace="com.czxy.mybatis.mapper.UserMapper">
<select id="selectUserByID" resultType="user">
select * from user where uid=#{uid}
select>
mapper>
public interface UserMapper extends Mapper<User> {
/**
* 使用xml式实现自定义查询
* */
public User selectUserByID(Integer uid);
}
public class OrderTest {
/**
* 使用xml式实现自定义查询
* **/
@Test
public 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();
}
}