mysql test 映射到实体_【长文】Spring学习笔记(七):Mybatis映射器+动态SQL

本文详细介绍了MyBatis中映射器的配置与使用,包括基本的查询、插入、更新和删除操作,以及如何传递多个参数。此外,还深入探讨了动态SQL,如if、choose、trim、where、set、foreach和bind标签的用法,帮助读者更好地理解和掌握MyBatis的动态SQL功能。
摘要由CSDN通过智能技术生成

1 概述

本文主要讲述了如何使用MyBatis中的映射器以及动态SQL的配置。

2 MyBatis配置文件概览

MyBatis配置文件主要属性如下::相关设置,键值对形式

:类型别名

:类型处理器

:对象工厂

:插件,包含若干个

:环境配置,包含若干个,在中可以指定事务管理器以及数据源

:数据库厂商标识

:映射器,包含若干个

注意顺序不能颠倒,否则启动时会发生异常。

3 准备步骤

由于本文大部分的代码都只给出了关键的语句而没有完整的工程,因此如果想要实现一遍请clone此处的代码(Kotlin请clone此处),并:利用resources/sql下的脚本文件创建数据库以及数据表,并插入相应数据

修改MyBatis、Spring、dhcp2等依赖为最新版本并修改MySQL驱动为对应版本

修改applicationContext.xml文件中的数据库用户名,密码以及数据库URL,可能需要修改驱动

开启数据库服务并进行测试,运行MainTest中的测试方法即可,正常来说会出现如下结果:

4 映射器概述

MyBatis的映射器由一个接口加上XML映射文件组成,是最复杂的组件,映射文件常用元素如下::查询语句

//:插入/更新/删除语句,返回操作所影响的行数,比如插入了两行,操作成功了影响的行数则为两行,返回整数2

:自定义的SQL

:提供映射规则

下面先来看一下最常用的。

4.1

示例(在mapper/UserDao.xml直接添加即可):

select * from user where id = #{id}

其中id是唯一标识符,接受一个Integer,返回com.pojo.User对象,结果集自动映射到com.pojo.User中。

常用属性如下:id:语句的全局唯一标识符

paramterType:表示传入SQL语句的参数类型的全限定名或别名,可选,能自动推断

resultType:执行SQL后返回的类型

resultMap:与resultType类似,resultType默认一一对应映射,比如表字段名为id,则映射到实体类的id中,而resultMap需要手动定义映射关系,这样就可以把表字段中的id映射到实体类的id1,或id2,或id3,resultType与resultMap两者需要指定一个,不能同时存在

flushCache:设置调用SQL后是否要求MyBatis清空之前查询的本地缓存以及二级缓存,默认false

useCache:启动二级缓存,默认true

timeout:超时参数,单位秒

fetchSize:获取记录的总条数设定

statementType:使用哪个JDBC的Statement,取值可以为STATEMENT/PREPARED/CALLABLE,分别表示Statement/PreparedStatement/CallableStatement

resultSetType:针对JDBC的ResultSet,可设置为FORWARD_ONLY/SCROLL_SENSITIVE/SCROLL_INSENSITIVE,分别表示只允许向前访问/双向滚动,不及时更新/双向滚动,及时更新

并修改UserDao,添加一个selectById方法:User selectById(Integer id);

可以直接测试了:@Test

public void selectById()

{

System.out.println(dao.selectById(1));

}

下面来看一下如何传递多个参数。

4.2 传递参数

有了最基本的select后,传递id这种单一参数很容易,但是实际情况中很多时候需要传递多个参数,MyBatis中传递多个参数有两种方式:通过Map传递

通过JavaBean传递

4.2.1 Map

可以使用Map传递多个参数,示例如下:

select * from user where name like concat('%', #{name}, '%') and age = #{age}

参数名name以及age是Map的键。

接着在UserDao下添加:User selectByMap(Map map);

然后在主类中使用Map添加键值对:@Test

public void selectByMap()

{

Map map = new HashMap<>();

map.put("name","111");

map.put("age","33");

System.out.println(dao.selectByMap(map));

}

这样就能传递多个参数进行查询了。

4.1.2 使用JavaBean

传递多个参数的另一种方法是利用JavaBean传递,创建一个POJO类:@Getter

@Setter

@Builder

@ToString

public class UserPOJO {

private String name;

private Integer age;

}

修改UserDao接口方法:public User selectByPOJO(UserPOJO user)

接着修改映射文件,实际上修改parameterType即可:

select * from user where name like concat('%', #{name}, '%') and age = #{age}

注意访问传递的参数时直接使用POJO类的属性名即可,无须加上类似UserPOJO.的前缀。

最后进行测试:@Test

public void selectByPOJO()

{

UserPOJO pojo = UserPOJO.builder().age(33).name("111").build();

System.out.println(dao.selectByPOJO(pojo));

}

4.2

用于插入,大部分属性与相同,下面是几个特有属性:keyProperty:将插入操作的返回值赋给POJO类的某个属性

keyColumn:用于设置主键列的位置,当表中第1列不是主键时需要设置该参数,联合主键可以使用逗号分隔

useGeneratedKeys:使用JDBC的getGeneratedKeys获取数据库内部产生的主键,默认false

比如典型的主键回填如下:

insert into user(name, age) values (#{name}, #{id})

这样就会利用数据库生成的自增主键回填到User的id属性中,UserDao接口如下:int insertUser1(User user);

一般来说插入操作返回一个整数,表示操作影响的行数,因此可以设置返回值为int,测试如下:@Test

public void insertUser1()

{

User user = User.builder().age((short) 88).name("test1").build();

System.out.println(dao.insertUser1(user));

System.out.println(user.getId());

}

另外如果不支持自增主键,可以使用selectKey自定义生成主键,比如:

select if(max(id) is null,1,max(id)+1) as newId from user

insert into user(id,name,age) values(#{id},#{name},#{age})

中的keyProperty指定了新主键newId返回给pers.pojo.User的id属性,order设置执行顺序,BEFORE/AFTER表示执行之后/之前再执行插入语句。

测试:@Test

public void insertUser2()

{

User user = User.builder().age((short) 10).name("test2").build();

System.out.println(dao.insertUser2(user));

System.out.println(user.getId());

}

4.3 /

返回一个整数,属性与/类似,简单示例如下:

update user set name=#{name}, age=#{age} where id = #{id}

delete from user where id = #{id}

同理update/delete返回一个整数,表示操作影响的行数,因此设置UserDao接口如下:int updateUser(User user);

int deleteUser(Integer id);

测试:@Test

public void updateUser()

{

User user = User.builder().id(3).name("3333333").age((short)11).build();

selectAll();

System.out.println(dao.updateUser(user));

selectAll();

}

@Test

public void deleteUser()

{

selectAll();

System.out.println(dao.deleteUser(3));

selectAll();

}

4.4

用于定义SQL的一部分,以方便后面的SQL语句引用,比如:

id,name,age

select from user

UserDao接口:List selectBySqlColumn();

测试:@Test

public void selectBySqlColumn()

{

System.out.println(dao.selectBySqlColumn());

}

5

上面提高过,比要强大,但是需要手动定义映射关系,一个常见的如下:

5.1 使用Map

查询SQL的结果可以使用Map/POJO存储,使用Map存储不需要手动编写,默认表属性名是键值对的键:

select * from user

可用List来接收返回结果,一条记录映射到一个Map对象,Map中的key是select的字段名。

示例的UserDao方法如下:List> selectReturnMap();

其中Map类型为Map,测试方法如下:@Test

public void selectReturnMap()

{

dao.selectReturnMap().forEach(System.out::println);

}

5.2 使用POJO

如果使用POJO存储返回的对象时,需要先定义一个POJO类,可以在上面的UserPOJO基础上加上一个id属性:@Getter

@Setter

@Builder

@ToString

public class UserPOJO {

private Integer id;

private String name;

private Integer age;

}

接着编写映射文件:

其中property指定POJO的属性,column是表字段名,最后配合使用,指定resultMap为对应id:

select * from user

返回结果可以用List接收:List selectReturnPOJO();

测试方法:@Test

public void selectReturnPOJO()

{

dao.selectReturnPOJO().forEach(System.out::println);

}

6 级联查询

级联查询就是利用主键与外键的关系进行组合查询,比如表A的一个外键引用了表B的一个主键,查询A时,通过A的外键将B的相关记录返回,这就是级联查询。常见的级联查询有三种:一对一

一对多

多对多

MyBatis支持一对一以及一对多级联,没有对多对多级联提供支持,但是可以用多个一对多级联实现多对多级联。下面分别来看一下。

6.1 一对一

一对一级联查询是最常见的级联查询,可以通过中的进行配置,通常使用的属性如下:property:映射到实体类的对象属性

column:指定表中对应的字段

javaType:指定映射到实体对象属性的类型

select:指定引入嵌套查询的子SQL语句,用于关联映射中的嵌套查询

下面通过一个例子进行说明,例子分五步:创建数据表

创建实体类

编写映射文件

修改持久层接口

添加测试方法

6.1.1 数据表

为了方便新增表以及数据都写在一起:use test;

drop table if exists idcard;

drop table if exists person;

create table idcard(

id int(10) primary key auto_increment,

code char(18) collate utf8mb4_unicode_ci default null

);

create table person(

id int(10) primary key,

name varchar(20) collate utf8mb4_unicode_ci default null,

age smallint default null,

idcard_id int(10) default null,

key idcard_id(idcard_id),

constraint idcard_id foreign key (idcard_id) references idcard(id)

);

insert into idcard(`code`) values('123456789123456789');

insert into person(`id`,`name`,`age`,`idcard_id`) values (1,'111',22,1);

6.1.2 实体类@Data

public class IdCard {

private Integer id;

private String code;

}

@Data

public class Person {

private Integer id;

private String name;

private Integer age;

private IdCard card;

}

另外还需要创建一个映射结果的POJO类:@Data

public class PersonPOJO {

private Integer id;

private String name;

private Short age;

private String code;

}

6.1.3 映射文件

映射文件分为两个:IdCardMapper.xml

PersonMapper.xml

首先是IdCardMapper.xml,加上一个即可,注意namespace的位置填写正确,对应dao的位置。<?xml version="1.0" encoding="UTF-8"?>

select * from idcard where id = #{id}

其次是PersonMapper.xml:<?xml version="1.0" encoding="UTF-8"?>

select="pers.oneToOne.dao.IdCardDao.selectCodeById"/>

select *

from person

where id = #{id}

select p.*, ic.code

from person p,

idcard ic

where p.idcard_id = ic.id

and p.id = #{id}

select p.*, ic.code

from person p,

idcard ic

where p.idcard_id = ic.id

and p.id = #{id}

首先第一个先指定id等属性,接着是:property是实体类属性,注意类型为IdCard

column是表字段名,类型为int(10)

javaType是通过后面的select返回的类型,可以理解成是property的类型,也就是IdCard

select指定嵌套查询使用的SQL,对应于IdCardDao.xml中的selectCodeById

接着在一个中的resultMap指定该map的id即可。使用这种方法执行的是两次SQL:一次是select * from person where id=?

一次是select * from idcard where id=?

最后再把结果整起起来,开启调试可以发现实际上也是执行了两条SQL:

而第二个中,在里面没有了select属性,直接将结果映射到SelectPersonById中,这是执行一条SQL语句的结果:select p.*,ic.code from person p,idcard ic where p.idcard_id = ic.id and p.id=#{id}

实际查询如下:

如果需要重要可以将其配置成,比如:

而最后一个是进行连接查询,无需额外的,实际执行情况如下:

6.1.4 Dao接口

这个比较简单:public interface PersonDao {

Person selectPersonById1(Integer id);

Person selectPersonById2(Integer id);

PersonPOJO selectPersonById3(Integer id);

}

6.1.5 测试@Test

public void selectPersonById()

{

System.out.println(dao.selectPersonById1(1));

System.out.println(dao.selectPersonById2(1));

System.out.println(dao.selectPersonById3(1));

}

注意在测试之前,需要修改配置文件mybatis-config.xml:

前两个表示开启延迟加载以及按需加载,后面一个是设置调试开关,最后在下面的加上对应的xml的位置。

要注意的一个是需要写在的前面。

另外因为Dao接口没有加上@Mapper注解,因此需要在applicationContext.xml中手动加上Dao位置:

测试结果:

6.2 一对多

一对多的级联查询与一对一处理有相似之处,主要是映射文件中的配置,例子也是和上面一样分五步。

6.2.1 数据表

需要两张表:user

order

user可以沿用前面的user表,而order表如下:use test;

drop table if exists orders;

create table orders(

id int(10) primary key auto_increment,

ordersn varchar(10) collate utf8mb4_unicode_ci default null,

user_id int(10) default null,

key user_id(user_id),

constraint user_id foreign key (user_id) references user(id)

);

insert into orders(`ordersn`,`user_id`) values ('testorder1',1),('testorder2',1),('testorder3',1);

6.2.2 实体类

添加实体类Orders:@Data

public class Orders {

private Integer id;

private String ordersn;

}

同时创建一个带Orders的User:@Data

@Builder

@NoArgsConstructor

@AllArgsConstructor

public class UserWithOrders {

private Integer id;

private String name;

private Short age;

private List ordersList;

}

6.2.3 映射文件

两个:OrdersMapper.xml

UserWithOrdersMapper.xml

首先是OrdersMapper.xml,只有一个简单的:<?xml version="1.0" encoding="UTF-8"?>

select * from orders where user_id=#{id}

接着是UserWithOrdersMapper.xml:<?xml version="1.0" encoding="UTF-8"?>

select * from user where id=#{id}

select u.*,o.id,o.ordersn from user u,orders o where u.id = o.user_id and u.id = #{id}

select u.*,o.id,o.ordersn from user u,orders o where u.id = o.user_id and u.id = #{id}

相比起一对一的级联,重点改变的就是其中的,重要属性如下:property:指定实体类的属性字段

ofType:指定集合中的类型

column:将哪些值传递给select中的方法

select:嵌套查询的语句

第二个类似,将查询的结果直接映射到Orders的属性上面。最后一种是直接使用连接查询。

6.2.4 Dao接口public interface OrdersDao {

List selectOrdersById(Integer id);

}

public interface UserWithOrdersDao {

UserWithOrders selectUserOrders1(Integer id);

UserWithOrders selectUserOrders2(Integer id);

List selectUserOrders3(Integer id);

}

6.2.5 测试@Test

public void selectUserOrders()

{

System.out.println(dao.selectUserOrders1(1));

System.out.println(dao.selectUserOrders2(1));

System.out.println(dao.selectUserOrders3(1));

}

6.3 多对多

MyBaits其实不支持多对多级联,但是可以通过多个一对多级联实现,比如一个订单对应多个商品,一个商品对应多个订单,这样两者就是多对多级联关系,这样使用一个中间表,就可以转换为两个一对多关系。

下面同样通过五个步骤实现多对多级联。

6.3.1 数据表

需要订单表、商品表以及一个中间表,由于订单表Orders之前已创建,这里只需要创建两个表:use test;

create table product(

id int(10) primary key auto_increment,

name varchar(10) collate utf8mb4_unicode_ci default null,

price double default null

);

create table orders_detail(

id int(10) primary key auto_increment,

orders_id int(10) default null,

product_id int(10) default null,

key orders_id(orders_id),

key product_id(product_id),

constraint orders_id foreign key (orders_id) references orders(id),

constraint product_id foreign key (product_id) references product(id)

);

insert into product(`name`,`price`) values('product1',1.1),('product2',2.2),('product3',3.3);

insert into orders_detail(`orders_id`,`product_id`) values(1,1),(1,2),(1,3),(2,1),(2,3);

6.3.2 实体类

订单类可以沿用之前的,只需要两个实体类:@Data

@AllArgsConstructor

@NoArgsConstructor

public class Product {

private Integer id;

private String name;

private Double price;

private List orders;

}

@Data

@AllArgsConstructor

@NoArgsConstructor

public class OrdersWithProduct {

private Integer id;

private String ordersn;

private List products;

}

6.3.3 映射文件<?xml version="1.0" encoding="UTF-8"?>

select o.*,p.id as pid ,p.name,p.price from orders o,orders_detail od, product p where o.id = od.orders_id and od.product_id = p.id

这里的多对多级联实质上是通过每次指定不同的OrdersId去查询对应的Product实现的,也就是分成了多次的一对多级联。

6.3.4 Dao接口public interface OrdersWithProductDao {

List selectOrdersAndProduct();

}

6.3.5 测试@Test

public void test()

{

ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

OrdersWithProductDao dao = context.getBean(OrdersWithProductDao.class);

System.out.println(dao.selectOrdersAndProduct());

}

7 动态SQL

最后来看一下动态SQL,动态SQL可以避免手动拼接SQL语句,比如在某些条件成立的情况下添加and xxx=xxxx之类的操作。先来看一下最常用的。

7.1

类似Java中的if语句,最简单的例子如下:

select * from user where 1=1

and name like concat('%',#{name},'%')

and age = #{age}

也就是说当test中的条件成立时,便添加and xxx语句。注意test这个属性是必须的,不能省略。

(注这里用到了一个关键的1=1,仅作说明使用,实际开发请勿使用1=1进行拼接)

Dao接口:List selectByIf(User user);

测试:@Test

public void testIf()

{

System.out.println(dao.selectByIf(User.builder().age((short) 33).name("111").build()));

}

7.2 ++

类似Java中的switch语句:类似switch

类似case

类似default

当其中一个成立时,语句便结束,类似于自动加上了"break"。

示例:

select * from user where 1=1

and name like concat('%',#{name},'%')

and age=#{age}

and id != 3

Dao接口:List selectByChoose(User user);

测试:@Test

public void testChoose()

{

System.out.println(dao.selectByChoose(User.builder().age((short)33).build()));

}

7.3

主要功能:加前缀

加后缀

替换某些首部/尾部内容

这里是一个使用来实现的例子:

select * from user

and name like concat('%',#{name},'%')

Dao接口:List selectByTrim(User user);

测试:@Test

public void testTrim()

{

System.out.println(dao.selectByTrim(User.builder().build()));

System.out.println(dao.selectByTrim(User.builder().name("test2").build()));

}

7.4

最常用的就是拼接查询条件,比如有多个查询条件,仅仅使用多个的话会出现首个有一个多余的and的问题,而使用会进行智能处理,当然也对or适用,例子如下:

select * from user

and name like concat('%',#{name},'%')

and age=#{age}

Dao接口:List selectByWhere(User user);

测试:@Test

public void testWhere()

{

System.out.println(dao.selectByWhere(User.builder().build()));

System.out.println(dao.selectByWhere(User.builder().name("111").build()));

System.out.println(dao.selectByWhere(User.builder().age((short)-3).build()));

}

7.5

一般配合update语句使用,比如:

update user

name = #{name},

age = #{age}

where id=#{id}

Dao接口:int updateBySet(User user);

测试:@Test

public void testSet()

{

System.out.println(dao.updateBySet(User.builder().name("999999").age((short)39).id(1).build()));

System.out.println(dao.selectByWhere(User.builder().build()));

}

7.6

主要用于in中,可以认为是一个集合,典型的使用场景是select xxx from xxx where xxx in 。

的主要属性有:item:每个元素的别名

index:每个元素的下标

collection:的类型,有list、array、map三种,当传入单个参数且该参数类型为List时,则为list,传入单个参数且该参数类型为数组时,则为array,否则应将其封装成Map,并设置属性值为map

open:语句开始标志

close:语句结束标志

例子:

select * from user where id in

#{item}

Dao接口:List selectByForeach(List id);

测试:@Test

public void testForeach()

{

System.out.println(dao.selectByForeach(List.of(1,2,3)));

}

7.7

可用于对字符串进行拼接,对于字符串拼接,MySQL使用的是concat,而Oracle使用的是||,而MyBatis提供了可以屏蔽这种DBMS之间的差异,无需修改xml即可进行移植,例子如下:

select * from user where name like #{new_name}

Dao接口:List selectByBind(User user);

测试:@Test

public void testBind()

{

System.out.println(dao.selectByBind(User.builder().name("test1").build()));

}

8 源码

此处给出了实现所有例子后的代码,仅供参考,但不建议直接clone,建议从初始化工程开始逐步实现。

Java版:

Kotlin版:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值