MyBatis使用XML映射文件(mybatis的xml映射文件)

网友投稿 2483 2022-05-30

1.概述

这一次想把MyBatis的XML声明SQL的方式大概说一下。使用的demo可以参考:

《spring boot整合Mybatis3.5.4使用XML定义SQL》

MyBatis可以通过注解使用声明,也可以xml文件来声明SQL。前者简单,不灵活,后者不仅方便灵活,还方便优。通过XML来编写映射的SQL也是MyBatis所推荐的。MyBatis的一个映射器类就对象一个xml文件,xml文件写SQL语句。

xml文件的根元素是mapper,mppper元素可以包含以下子元素:

cache:指定的命名空间的缓存配置

cache-ref:引用其他命名空间缓存配置

resultMap:描述如何从数据库结果集中加载到对象中

parameterMap:在MyBatis3.5.4中弃用了!

sql:定义可重用的SQL块

insert:用于声明INSERT语句

update:用于声明UPDATE语句

delete:用于声明DELETE语句

select:用于声明SELECT语句

下面逐一讲解。

2.元素讲解

2.1.cache

默认情况下:

1

只启用本地会话缓存,只在会话期间缓存数据。有如下特点:

所有查询出来的结果集都会被缓存起来;

所有在映射声明文件中的insert、update、delete语句都会被放到缓存里;

使用最近最少使用的(LRU)逐出算法 ;

缓存不会按任何基于时间的计划刷新

缓存可以存储1024个列表或对象的个引用;

缓存可读可写,意味着缓存的对象是不共享的,因此能够被调用者安全地修改,因为不存在其他调用者或线程潜在的修改。

缓存配置只会对cache标记所在的映射文件中的语句有起作用。如果不想用默认的配置,可以修改以上缓存的属性,如:

1

2

3

4

5

创建一个FIFO缓存,并且每隔60秒就刷新一次,最多可以存储512个返回的结果对象或列表或对象的引用,而且缓存只能读不能写。 因为写可能会在多个调用者或线程之间才生冲突。

缓存用的逐出策略有以下这些:

LRU(默认的):最近最少使用,移除那些长时间不使用的对象

FIFO:先进先出, 按照他们进入缓存的顺序移除对象

SOFT:软引用,基于垃圾收集器状态和软引用移除对象。

WEAK:弱引用, 更积极地基于垃圾收集器状态和弱引用规则移除对象

cache元素的属性:

使用自定义的cache:

1

自定义cache要实现org.apache.ibatis.cache.Cache接口,MyBatis的Cache接口是这样的:

public interface Cache { String getId(); int getSize(); void putObject(Object key, Object value); Object getObject(Object key); boolean hasKey(Object key); Object removeObject(Object key); void clear(); }

1

2

3

4

5

6

7

8

9

实现Cache接口:

public class MyCustomCache implements Cache{ // ... }

1

2

3

点到为止,更多内容请到网上查阅!

2.2.cache-ref

引用其他命名空间的缓存配置。

1

2.3.resultMap

resultMap是MyBatis中最重要的元素,因为将结果集映射到对象中,基本都用resultMap来完成。使用resultMap可以让你省去90%的JDBC代码,resultMap其甚至可以实现一些JDBC做不到的事。MyBatis自动创建ResultMap,基于名称自动映射列到JavaBean的属性上, 如果列名和JavaBean的属性匹配不上,我们可以在列名上使用select子句别名(标准SQL特性)来创建标签匹配:

1

2

3

4

5

6

7

8

除了上面这种给列名取个与JavaBean属性匹配得了的别名外,还可以使用标签来建立数据库表的列名与JavaBean属性的对应关系:

(1)第一步:使用resultMap标签建立列名与属性名的对应关系

1

2

3

4

5

(2)第二步:引用resultMap

1

2

3

4

5

resultMap不能和resultType同时使用,继续阅读你就明白的了。

数据库很难做到时时刻刻都能够和我们的JavaBean的属性匹配上。而且不是所有数据库都能很好的实现数据库设计第三范式或BCNF范式。这些问题都使处有时并不能简单地通过自动映射来完成,对于这些复杂的映射关系的处理可以用resultMap来解决表列名与JavaBean字段映射的问题。这也是它存在的原因。举例说明,下面这个复杂的SQL的映射问题:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

从上面这个例子,我们可以看到resultMap标签有很多子标签,下面我们一个一个来看。

(1)constructor :在类实例化时将结果注入到类的构造函数中,如将结果果集的内容注入到下面这个类的构造函数中:

public class User { //... public User(Integer id, String username, int age) { //... } //... }

1

2

3

4

5

6

7

使用constructor时,要保证结果集内容的顺序要和构造函数的参数顺序一致,如下面的映射,MyBatis会搜索定义了三个参数(java.lang.Integer ,java.lang.String ,int)且出现顺序一样的构造函数:

1

2

3

4

5

如果不想维护arg元素的顺序,可以为每一个arg元素指定一个名称,使其与构造函数名称对应上(对应的注解方式是使用@Param ):

1

2

3

4

5

constructor元素的子元素:

(2)id & result:

1

2

这两个是结果映射中最基本的元素。id和result都是映射一个列值到一个简单类型(String, int, double, Date等)的属性或字段。 它们唯一的区别是id结果标识为一个标识符属性,在比较对象实例时使用,有助于提高总体性能。特别是提高缓存和嵌套result映射的性能,如SQL中带join语句的映射。

它们有以下属性:

JDBC支持的类型:

(3)association:复杂类型的组合,它是处理 “has-one” 这种关系的,例如Blog类中有个Author类型时的属性。MyBatis有以下两种方式来实现association加载:

嵌套Select:通过执行另一个SQL语句来返回这种嵌套的复杂类型,但这种式对大数量的查询的性能不是很好。

1

2

3

4

5

6

7

8

9

10

11

嵌套result:

先介绍association的一些属性:

下面是嵌套result的例子:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

上面我们把author独立写了个resultMap这样author部分就可以重用了,如果我们不想重用,可以将其写在一起,如:

1

2

3

4

5

6

7

8

9

10

11

如果Blog中有两个字段author、coauthor,它们都是Author对象,resultMap又该如何写呢?select语句如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

resultMap可以这样写:

1

2

3

4

5

6

association处理多结果集(调用存储过程):从MyBatis 3.2.3开始支持。有一些数据库允许调存储过程来执行一或多条语句,并返回一或多个结果集。这种可以不使用join连接查询。假设我们调用数据库里存储过程来执行以下查询并返回两个结果集(blogs,authors):

SELECT * FROM BLOG WHERE ID = #{id} SELECT * FROM AUTHOR WHERE ID = #{id}

1

2

在xml映射文件中,使用resultSets属性指定结果集名称,多个结果集名称之间用逗号分隔:

1

2

3

现在我们可以把authors结果集数据填充到 “author” association中:

1

2

3

4

5

6

7

8

9

10

11

(4)collection:复杂类型的集合。collection元素与association元素很像,但后者是解决“has-one",前者是解决”has-many“这种关系。举个例子:

Blog有许多Posts(帖子),在Blog类里,帖子被定义列表:

private List posts;

1

那么resultMap就应该这样写:

1

2

3

4

5

与association元素一样,collection元素也有两种方式来处理结果集:

嵌套select:

1

2

3

4

5

6

7

8

9

10

嵌套result:

首先,来看看根据blog id拿帖子的SQL:

1

2

3

4

5

6

7

8

9

10

11

12

resultMap我们可以这样写:

1

2

3

4

5

6

7

8

9

10

也可以将Post的resultMap单独写,以便重用:

1

2

3

4

5

6

7

8

9

10

11

collection处理多个resultSet的情况(调用存储过程):

和前面association一样,我们可以调用一个存储过程来执行两个查询,并返回两个结果集,一个是Blogs的,别一个是Posts的:

SELECT * FROM BLOG WHERE ID = #{id} SELECT * FROM POST WHERE BLOG_ID = #{id}

1

2

在xml映射文件中,使用resultSets指定每个结果集的名字,多个结果集名称之间用逗号隔开:

1

2

3

将posts集合填充到对象:

1

2

3

4

5

6

7

8

9

collection的相关属性:

(5)discriminator :

1

2

3

有时候一个数据库查询可能会返回许多不同的数据类型的结果集。discriminator元素就是设计用来处理这种情况的。discriminator有点java中的switch语句。discriminator的定义指定column和javaType属性,column就是MyBatis将要对比的值所在的地方。javaType是确保对比的类型正确。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

与下面是等价的:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

在上面这个例子里,MyBatis会收到结果集中每条记录,然后对比它的vehicle_ type的值,如果它匹配上了discriminator cases,那么就用指定的resultMap。如果没有一个匹配上,那么MyBatis将使用定义在discriminator块外的resultMap。carResult定义如下,如果vehicle_type是1,那么它就被使用:

1

2

3

我们知道car也是vehicle,所以我们想使用carResult的同时也把vehicleResult的字段加载进来,这其实就是car继承vehicle,那么resultMap也是可以继承的,上面的carResult可以继承vehicleResult,这样也会把vehicleResult的字段加载进来:

1

2

3

2.4.sql

这个元素是用于定义一些可重用的SQL片段,这些片段可以被包含在其他的语句中。如定义以下SQL片段:

${alias}.id,${alias}.username,${alias}.password

1

在select语句中包含此片段:

1

2

3

4

5

6

7

8

9

10

11

属性值也可以用于include refid属性里,或者include子句内的属性值,如:

${prefix}Table from

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

2.5.insert、update、delete

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

insert、update、delete的属性:

列子:

insert into Author (id,username,password,email,bio) values (#{id},#{username},#{password},#{email},#{bio}) update Author set username = #{username}, password = #{password}, email = #{email}, bio = #{bio} where id = #{id} delete from Author where id = #{id}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

insert语句自动生成id列:

insert into Author (username,password,email,bio) values (#{username},#{password},#{email},#{bio})

1

2

3

4

5

如果数据库支持同时多行插入,那么我们可以给对象传个list或数组:

insert into Author (username, password, email, bio) values (#{item.username}, #{item.password}, #{item.email}, #{item.bio})

1

2

3

4

5

MyBatis使用XML映射文件(mybatis的xml映射文件)

6

如果数据库不支持自动生成列类型或者还不支持JDBC驱动自动生成keys,那么可以使用以下selectKey方式来生成key,下面这个列子是随机生成一个ID:

select CAST(RANDOM()*1000000 as INTEGER) a from SYSIBM.SYSDUMMY1 insert into Author (id, username, password, email,bio, favourite_section) values (#{id}, #{username}, #{password}, #{email}, #{bio}, #{favouriteSection,jdbcType=

1

2

3

4

5

6

7

8

9

selectKey子元素:

1

2

3

4

5

2.6.select

SELECT * FROM PERSON WHERE ID = #{id}

1

2

3

2.7.参数类型

一般来说,我们可以通过parameterType指定参数类型,如果指定的类型与数据库中的类型不一样的话,MyBatis提供一种方式给我们指定参数类型,格式:

#{property,javaType=int,jdbcType=NUMERIC}

1

javaType通常由参数对象决定,除非对象是一个HashMap。指定javaType可以确保使用正确的TypeHandler。jdbcType是 JDBC需要的,用于所有可以为空的列,如果null作为一个值传递的话,就应该指定jdbcType。

我们还可以指定TypeHandler类或别名:

#{age,javaType=int,jdbcType=NUMERIC,typeHandler=MyTypeHandler}

1

对于数字类型,有一个numericScale可以决定精确到小数点后几位:

#{height,javaType=double,jdbcType=NUMERIC,numericScale=2}

1

mode模式属性可以是IN 、OUT、INOUT,如果是OUT或INOUT,那么参数对象属性的真实值会被改变。如果模式是OUT或INOUT ,并且jdbcType=CURSOR

,那么你必须指定一个resultMap来映射ResultSet到参数类型,此时javaType属性是可选的:

#{department, mode=OUT, jdbcType=CURSOR, javaType=ResultSet, resultMap=departmentRes}

1

MyBatis也支持高级数据类型,如结构体structs,但你必须告知语句类型名字:

#{middleInitial, mode=OUT, jdbcType=STRUCT, jdbcTypeName=MY_TYPE, resultMap=departmentRes}

1

大多数时候,我们都只简单地指定属性名,然后MyBatis就会帮我们搞掂剩下的这些。因此,我们只需要为可空的列指定jdbcType就可以了,如:

#{firstName} #{middleInitial,jdbcType=VARCHAR} #{lastName}

1

2

3

2.8.置换字符串

默认情况下,使用#{}语法,会让MyBatis生成PreparedStatement属性,并将值安全地设置到PreparedStatement的参数里。这种方式既安全又快速。有时,我们想直接在SQL语句注入未修改的字符串,当SQL语句中的元数据(如表名,列名)是动态变化的,字符串置换就显得很有用了。例如,你要从一张表中通过任意一个列来选择:

@Select("select * from user where ${column} = #{value}") User findByColumn(@Param("column") String column, @Param("value") String value);

1

2

接受从用户输入或提供SQL语句中未修改的元数据名称是不安全的。这存在潜在的SQL注入攻击。因此如果使用这种方式,我们要做到不允许用户输入这些字段,我们自己应该做好转义和检查,以此来提高安全性。

3.别名

使用别名,你就不需要写全限定路径了。

在springboot的application.yml加入以下配置:

mybatis: # 指定MyBatis的配置文件位置 config-location: classpath:config/mybatis-mapper-config.xml # 指定映射器的xml文件的位置 mapper-locations: classpath:mybatis/mapper/*.xml

1

2

3

4

5

~/Desktop/MyBatisXMLDemo$ touch src/main/resources/config/mybatis-mapper-config.xml ~/Desktop/MyBatisXMLDemo$ touch src/main/resources/mybatis/mapper/PersonMapper.xml

1

2

1

2

3

4

5

6

7

1

2

3

4

5

6

7

8

9

10

11

12

还可以把数据库连接的信息从application.yml文件中移到MyBatis的配置文件mybatis-mapper-config.xml中:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

4.动态SQL

MyBatis可以实现动态SQL。

if

choose (when, otherwise)

trim (where, set)

foreach

4.1.if

1

2

3

4

5

6

7

再如:

1

2

3

4

5

6

7

8

9

4.2.choose (when, otherwise)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

4.3.trim (where, set)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

where元素可以很方便处理SQL的where子句,当where元素中的if元素有符合的即需要插入where子句的),where元素就会在SQL语句后面添加where子句。如果where子句是以 “AND” 或 "OR"开头,它会把它去掉,再添加到where后面。如果where元素都还满足不了你的需求,Mybatis提供trim元素让你来自定义,如下面的trim等价于where元素:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

prefix是要插入的,prefixOverrides是要移除的。prefixOverrides属性接受要重写的以管道分隔的文本列表,注意空格的地方,它是必要的。简单点来说,trim的结果就是prefix属性的值加上trim元素里的值,如果trim里没有值,则不在SQL里插入,如果trim里的值是以prefixOverrides里的值开头的则先移除再和prefix属性值一起插入到SQL语句里。

有一种相似的动态update语句的解决方案叫set。set元素可以用来动态包含要更新的列,而排除不需要更新的列:

update Author username=#{username}, password=#{password}, email=#{email}, bio=#{bio} where id=#{id}

1

2

3

4

5

6

7

8

9

10

上面也可以用trim来自定义,如:

update Author username=#{username}, password=#{password}, email=#{email}, bio=#{bio} where id=#{id}

1

2

3

4

5

6

7

8

9

10

11

4.4.foreach

1

2

3

4

5

6

7

8

foreach元素允许我们指定一个集合collection,声明item和index变量,这两个变量可以在foreach元素内部使用。foreach也允许我们指定开始和结束的字符串,和添加在迭代之间的分隔符 。foreach元素不会意外地附加额外的分隔符,这一点可以放心。

我们可以传递任何迭代对象,如List、Set等,同样也可传递Map或Array对象到foreach作为集合参数。当使用Iterable 或 Array,index表示当前迭代的下标,item表示当前的值。当使用Map,如 Map.Entry 对象的集合,index就是key对象,item就是值对象。

4.5.script

在映射器类里使用注解的方式使用动态SQL,需要使用script 元素:

@Update({""}) void updateAuthorValues(Author author);

1

2

3

4

5

6

7

8

9

10

11

4.6.bind

bind元素可以让你在OGNL表达式之外,创建一个变量,并将其绑定到上下文中:

1

2

3

4

5

5. 多数据库供应商支持

说白了就是连接多个数据库。如果有一个databaseIdProvider配置了“_databaseId"变量并且对于动态代码来说是可用的。那么,我们可以根据不同的数据库提供商来建不同的语句,如:

select seq_users.nextval from dual select nextval for seq_users from sysibm.sysdummy1" insert into users values (#{id}, #{name})

1

2

3

4

5

6

7

8

9

10

11

上面大概把MyBatis使用xml映射文件说了一遍。

MyBatis XML

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:华为防火墙配置(防火墙NAT)(华为防火墙配置(防火墙基础))
下一篇:Lock和synchronized比较详解(谈谈synchronized和lock的区别)
相关文章