好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

MyBatis中#{}占位符与${}拼接符的用法说明

1、关于#{}占位符

先来看以下的示例,该示例是MyBatis中的SQL映射配置文件(Mapper配置文件),在该配置中使用了#{}占位符。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

<?xml version= "1.0" encoding= "UTF-8" ?>

<!DOCTYPE mapper

     PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"

     "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >

<mapper namespace= "test" >

   <! -- 根据用户编号,查询单个用户实体 -->

   < select id= "findUserById" parameterType= "int" resultType= "com.pjb.mybatis.po.User" >

     SELECT * FROM tb_user WHERE id = #{id}

   </ select >

 

   <! -- 新增用户 -->

   < insert id= "insertUser" parameterType= "com.pjb.mybatis.po.User" >

     INSERT INTO tb_user(user_name,blog_url,remark)

     VALUES (#{userName},#{blogUrl},#{remark});

   </ insert >

</mapper>

在SQL映射配置文章中,输入参数需要用占位符来标识对应的位置。

在传统的JDBC编程中,占位符用[?]来标识,然后在加载SQL之前按照[?]的位置设置参数。

而[#{}]在MyBatis中也代表一种占位符,该符号接受输入参数,在大括号中编写参数名称来接受对应参数。

[#{}]接受的输入参数的类型可以是简单类型、普通JavaBean或者HashMap。

当接受简单类型时,[#{}]中可以写[value]或者其他任意名称。

如果接受的是JavaBean,它会通过OGNL读取对象中的属性值。

2、关于${}拼接符

再来看以下的示例,该示例是MyBatis中的SQL映射配置文件(Mapper配置文件),在该配置中使用了${}拼接符。

?

1

2

3

4

5

6

7

8

9

10

<?xml version= "1.0" encoding= "UTF-8" ?>

<!DOCTYPE mapper

     PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"

     "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >

<mapper namespace= "test" >

   <! -- 根据用户名称,模糊查询用户列表 -->

   < select id= "findUserByUserName" parameterType= "java.lang.String" resultType= "com.pjb.mybatis.po.User" >

     SELECT * FROM tb_user WHERE user_name LIKE '%${value}%'

   </ select >

</mapper>

在SQL映射配置文件中,有时候需要拼接SQL语句。例如在模糊查询的时候,就需要在查询条件的两侧拼接两个[%]字符串,这个时候如果使用[#{}]占位符是不行的。在MyBatis中,[${}]在SQL配置文件中代表一个[拼接符号],可以在原有SQL语句上拼接新的符合SQL语法的语句。

但是要注意的是,使用[${}]拼接的SQL语句,会引起SQL注入,所以一般不建议使用[${}]。

[${}]接受输入参数的类型可以是简单类型、普通JavaBean或者HashMap。

当接受简单类型时,[${}]中只能写[value],而不能写其他任意名称。

如果接受的是JavaBean,它会通过OGNL读取对象中的属性值。

另外,在MyBatis 3.4.2之后,还可以在[${}]拼接符中设置一个默认值,格式如下:

${属性:默认值}

即在所需引入的属性名的后面添加[:]引号,然后紧跟着填写属性不存在或为空时的默认值。

【示例】设置[${}]拼接符的默认值。

(1)创建数据库连接的属性文件(db.properties)

在src目录下创建db.propertie属性文件,并使用[#]符号将属性项jdbc.username和jdbc.password注释掉,表示这两个属性项未进行配置。

?

1

2

3

4

jdbc.driver=com.mysql.cj.jdbc.Driver

jdbc.url=jdbc:mysql://localhost:3306/db_admin?useSSL= false &amp

#jdbc.username=root

#jdbc. password =123456

(2)启用拼接符默认值的配置

要使用[${}]拼接符中设置一个默认值,需要在properties标签中设置启用拼接符默认值的配置项。

在MyBatis配置文件(mybatis-config.xml)中,使用<properties>标签加载数据库连接属性文件(db.properties),并在该标签中设置启用拼接符默认值的配置项,该配置如下:

?

1

2

3

4

5

<!-- 加载用于数据库配置的属性文件 -->

< properties resource = "db.properties" >

   <!-- 启用默认值特性,这样${}拼接符才可以设置默认值 -->

   < property name = "org.apache.ibatis.parsing.PropertyParser.enable-default-value" value = "true" />

</ properties >

注意:在MyBatis配置文件(mybatis-config.xml)中,<properties>标签的配置必须写在<settings>标签的上面,因为MyBatis中的配置,不但有类型限制,还有顺序限制。

必须按照:<properties>、<settings>、<typeAliases>、<typeHandlers>、…顺序排放。

(3)编写数据库配置信息,并使用[${}]拼接符的默认值。

说明:

由于在db.propertie属性文件中,已经将jdbc.username和jdbc.password属性项注释掉了,然后在上面的配置信息中,给username和password配置项中的[${}]拼接符中设置默认值,这样程序在启动时,就会读取默认值。

补充:MyBatis映射——SQL占位符及传参

简介

本篇主要讲述Mybatis映射SQL通过#{}获取引入类型参数的属性值及通过@Param注解指定名称传参。

关于占位符与字符拼接:

占位符:占位符就是在某个地方占领一个位置,把它单独作为某个东西,比如这里就是把它作为 值。

#{}表示一个占位符号,通过#{}可以实现 preparedStatement 向占 位符中设置值, 自动进行 java

类型和 jdbc 类型转换。#{}可以有效防止 sql 注入。 #{}可以接 收简单类型值或 pojo 属性值。 如果 parameterType 传输单个简单类型值,#{} 括号中可以是 value 或其它名称。

字符拼接:字符拼接就是简单的对字符串拼接。没有特殊的其它含义。

表 示 拼 接 s q l 串 , 通 过 可 以 将 p a r a m e t e r T y p e 传 入 的 内 容 拼 接 在 s q l 中 且 不 进 行 j d b c 类 型 转 换 , 可 以 将 p a r a m e t e r T y p e 传 入 的 内 容 拼 接 在 s q l 中 且 不 进 行 j d b c 类 型 转 换 , 可 以 接 收 简 单 类 型 值 或 p o j o 属 性 值 , 如 果 p a r a m e t e r T y p e 传 输 单 个 简 单 类 型 值 , {}表示拼接 sql 串,通过可以将parameterType传入的内容拼接在sql中且不进行jdbc类型转换,可以将parameterType传入的内容拼接在sql中且不进行jdbc类型转换,{}可以接收简单类型值或 pojo 属性值,如果 parameterType 传输单个简单类型值,表示拼接sql串,通过可以将parameterType传入的内容拼接在sql中且不进行jdbc类型转换,可以将parameterType传入的内容拼接在sql中且不进行jdbc类型转换,可以接收简单类型值或pojo属性值,如果parameterType传输单个简单类型值,{}括号中只能是 value。

关于@Param:

在用注解来简化xml配置的时候(比如Mybatis的Mapper.xml映射文件中的sql参数引入);

@Param注解的作用是给参数命名,参数命名后就能根据名字得到参数值,正确的将参数传入sql语句中(一般通过#{}的方式,${}会有sql注入的问题)。

#{}: 解析为一个 JDBC 预编译语句(prepared statement)的参数标记符,一个 #{ } 被解析为一个参数占位符 。 ${}: 仅仅为一个纯碎的 string 替换,在动态 SQL 解析阶段将会进行变量替换。

MyBatis 的真正强大在于它的映射语句

Mapper.xml映射文件中定义了操作数据库的sql,每个sql是一个statement,映射文件是mybatis的核心。

映射文件中有很多属性,常用的就是parameterType(输入类型)、resultType(输出类型)、resultMap()、rparameterMap()。

实例步骤

先建好实体类Teacher和接口类

Teacher类

?

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

package com.lanou3g.mybaties.bean;

import lombok.Getter;

import lombok.Setter;

@Getter

@Setter

public class Teacher {

   private Integer id;

   private String tname;

   private Integer age;

   private Integer salary;

public Teacher(){}

   @Override

   public String toString() {

     return "Teacher{" +

         "id=" + id +

         ", tname='" + tname + '\ '' +

         ", salary=" + salary +

         ", remark='" + remark + '\ '' +

         ", age=" + age +

         '}' ;

   }

   private String remark;

   public Teacher(Integer id, String tname, Integer age, Integer salary, String remark) {

     this .id = id;

     this .tname = tname;

     this .age = age;

     this .salary = salary;

     this .remark = remark;

   }

   public Teacher(Integer id) {

     this .id = id;

   } 

}

接口类

?

1

2

3

4

5

6

7

8

9

10

11

12

13

package com.lanou3g.mybaties.dao;

import com.lanou3g.mybaties.bean.Teacher;

import org.apache.ibatis.annotations.Param;

import java.util.List;

public interface TeacherDao {

   List<Teacher> queryAll();

   Teacher queryById( int id);

   Teacher queryByIdAndAge( @Param ( "id" ) int id, @Param ( "age" ) int age);

   int insertTeacher(Teacher teacher);

   int insertTeacherByParam( @Param ( "tname" ) String tname, @Param ( "age" ) int age);

   int updateTeacherById(Teacher teacher);

   int deleteTeacherById( int id);

}

主要还是xml配置文件的配置,下面是 mybatis_conf.xml文件,它主要引入外部的properties文件(用于配置数据源)、定义类型别名(全局)、配置多套环境的数据库连接参数及引入哪些Mapper映射文件等

mybatis_conf.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

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

<? xml version = "1.0" encoding = "UTF-8" ?>

<!DOCTYPE configuration

     PUBLIC "-//mybatis.org//DTD Config 3.0//EN"

     "http://mybatis.org/dtd/mybatis-3-config.dtd">

< configuration >

   < properties resource = "jdbc.properties" />

   < settings >

   < setting name = "mapUnderscoreToCamelCase" value = "false" />

   </ settings >

   < typeAliases >

     <!--这样我们就可以在mybatis的的上下文中使用Teacher来代替全路径名了,减少配置的复杂度。 -->

     < typeAlias type = "com.lanou3g.mybaties.bean.Teacher" alias = "Teacher" />

   <!--default属性,这个属性作用就是指定当前情况下使用哪个数据库配置,

   也就是使用哪个<environment>节点的配置,

   default的值就是配置的<environment>标签元素的id值。-->

   < environments default = "test" >

     < environment id = "test" >

       <!-- 事务管理器:

      MANAGED: 这个配置就是告诉mybatis不要干预事务,具体行为依赖于容器本身的事务处理逻辑。

      JDBC: 这个配置就是直接使用了 JDBC 的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域。

     -->

       <!-- 使用jdbc事务管理 -->

       < transactionManager type = "JDBC" />

       <!-- 数据库连接池 -->

       < dataSource type = "POOLED" >

         < property name = "driver" value = "${jdbc.driver}" />

         < property name = "url" value = "${jdbc.url}" />

         < property name = "username" value = "${jdbc.user}" />

         < property name = "password" value = "${jdbc.password}" />

       </ dataSource >

     </ environment >

   </ environments >

   <!--<mappers>用来在mybatis初始化的时候,告诉mybatis需要引入哪些Mapper映射文件。-->

   < mappers >

     <!--通过class属性指定mapper接口名称,此时对应的映射文件必须与接口位于同一路径下,

     并且名称相同-->

    

     <!--通过resource属性引入classpath路径的相对资源-->

     < mapper resource = "mapper/TeacherMapper.xml" />

   

   </ mappers >

</ configuration >

TeacherMapper.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

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

<? xml version = "1.0" encoding = "UTF-8" ?>

<!DOCTYPE mapper

     PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"

     "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!-- namespace对应空Dao接口的全名 -->

<!--namespace属性

在MyBatis中,Mapper中的namespace用于绑定Dao接口的,即面向接口编程。

它的好处在于当使用了namespace之后就可以不用写接口实现类,

业务逻辑会直接通过这个绑定寻找到相对应的SQL语句进行对应的数据处理-->

< mapper namespace = "com.lanou3g.mybaties.dao.TeacherDao" >

   <!-- 此处的id是查询语句的名称,对应接口中的方法名 -->

   <!--指定 resultType 返回值类型时 Teacher 类型的,

     Teacher 在这里是一个别名,代表的是 com.lanou3g.mybaties.bean.Teacher

     对于引用数据类型,都是将大写字母转小写,比如 HashMap 对应的别名是 'hashmap'

     基本数据类型考虑到重复的问题,会在其前面加上 '_',比如 byte 对应的别名是 '_byte'

-->

   < select id = "queryAll" resultType = "Teacher" >

     <!--

      通过 resultType 指定查询的结果是 Teacher 类型的数据

      只需要指定 resultType 的类型,MyBatis 会自动将查询的结果映射成 JavaBean 中的属性

       -->

   select * from teacher;

  </ select >

   <!-- 带一个简单类型的参数, 这种情况下parameterType属性可以省略,

   mybatis可以自动推断出类型 -->

   < select id = "queryById" resultType = "Teacher" >

         select * from teacher where id = #{id};

   </ select >

   <!-- 带两个参数,需要在接口中通过@Param注解指定名称(因为编译时参数名不会保留) -->

   < select id = "queryByIdAndAge" resultType = "Teacher" >

     select * from teacher where id = #{id} and age &lt;= #{age};

   </ select >

   <!-- insert、update、delete的返回值都是int(影响行数) -->

   <!-- 自定义类型参数,通过#{属性名}可以直接获取引入类型参数的属性值 -->

   < insert id = "insertTeacher" parameterType = "Teacher" >

     insert into teacher(tname) values (#{tname});

   </ insert >

   < insert id = "insertTeacherByParam" >

     insert into teacher(tname, age) values (#{tname}, #{age});

   </ insert >

   < update id = "updateTeacherById" parameterType = "Teacher" >

     update teacher set tname = #{tname}, age = #{age} where id = #{id}

   </ update >

   < delete id = "deleteTeacherById" >

     delete from teacher where id = #{id};

   </ delete >

</ mapper >

前面完成后,就要测试了。不过在测试时需要实例化创建对象,因要实现多个方法,在这先建MyBatisTools.java工具类,其主要进行封装Mybatis初始化操作,要求支持创建多env sqlSessionFactory,整个应用生命周期内相同env的sqlSessionFactory对象只有一个(这个类不要过于探究,会用即可)。

MyBatisTools.java

?

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

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

package com.lanou3g.mybaties;

import lombok.extern.slf4j.Slf4j;

import org.apache.ibatis.io.Resources;

import org.apache.ibatis.session.ExecutorType;

import org.apache.ibatis.session.SqlSession;

import org.apache.ibatis.session.SqlSessionFactory;

import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;

import java.util.concurrent.ConcurrentHashMap;

/**

  * 封装Mybatis初始化操作

  * 支持创建多env sqlSessionFactory

  * 整个应用生命周期内相同env的sqlSessionFactory对象只有一个

  */

@Slf4j

public class MyBatisTools {

   private static ConcurrentHashMap<String, SqlSessionFactory> factoryMap = new MyConcurrentHashMap();

   private static MyBatisTools myBatisTools;

   private MyBatisTools() {}

   public static MyBatisTools getInstance() {

     if (myBatisTools == null ) {

       synchronized (MyBatisTools. class ) {

         if (myBatisTools == null ) {

           myBatisTools = new MyBatisTools();

         }

       }

     }

     log.debug( "当前一共有: " + factoryMap.size() + "个SqlSessionFactory实例" );

     log.debug( "他们分别是: " + factoryMap);

     return myBatisTools;

   }

   public SqlSessionFactory getSessionFactory(String env) {

     try {

       // 1. 读入配置文件

       InputStream in = Resources.getResourceAsStream( "mybatis_conf.xml" );

       // 2. 构建SqlSessionFactory(用于获取sqlSession)

       SqlSessionFactory sessionFactory = null ;

       synchronized (factoryMap) {

         if (factoryMap.containsKey(env)) {

           return factoryMap.get(env);

         } else {

           sessionFactory = new SqlSessionFactoryBuilder().build(in, env);

           factoryMap.put(env, sessionFactory);

         }

       }

       return sessionFactory;

     } catch (Exception e) {

       log.error( "初始化SqlSessionFactory失败" , e);

       return null ;

     }

   }

   public SqlSession openSession() {

     return getSessionFactory( null ).openSession();

   }

   public SqlSession openSession( boolean autoCommit) {

     return getSessionFactory( null ).openSession(autoCommit);

   }

   public SqlSession openSession(ExecutorType executorType, boolean autoCommit) {

     return getSessionFactory( null ).openSession(executorType, autoCommit);

   }

}

/**

  * 继承原生ConcurrentHashMap,处理null key问题

  */

class MyConcurrentHashMap extends ConcurrentHashMap {

   @Override

   public Object put(Object key, Object value) {

     if (key == null ) {

       key = "null" ;

     }

     return super .put(key, value);

   }

   @Override

   public boolean containsKey(Object key) {

     if (key == null ) {

       key = "null" ;

     }

     return super .containsKey(key);

   }

   @Override

   public Object get(Object key) {

     if (key == null ) {

       key = "null" ;

     }

     return super .get(key);

   }

}

最后就是测试,在AppTest类测试

AppTest

?

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

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

@Slf4j

public class AppTest

{

   /**

    * Rigorous Test :-)

    */

   TeacherDao teacherDao = null ;

   @Before

   public void setUp() { 

     teacherDao = MyBatisTools.getInstance().openSession( true ).getMapper(TeacherDao. class );

   }

   /**

    * 练习查询多个库(用到了多环境配置)

    */

   @Test

   public void textqueryById(){

     Teacher teacher=teacherDao.queryById( 1 );

     System.out.println(teacher);

   }

   @Test

   public void text(){

     List<Teacher> teachers=teacherDao.queryAll();

     System.out.println(teachers);

   }

   /**

    * 多个参数查询语句

    */

   @Test

   public void testQueryByIdAndAge() {

     Teacher teacherList = teacherDao.queryByIdAndAge( 1 , 65 );

     log.info( "查询结果:" + teacherList);

   }

   @Test

   public void testInsert() {

     // 新增Teacher表

     System.out.println( "--------------插入前:" );

     List<Teacher> teacherList = teacherDao.queryAll();

     System.out.println(teacherList);

     int ret = teacherDao.insertTeacher( new Teacher( "好人" ));

     log.info( "影响的行数: " + ret);

     // 比较low的写法(不推荐)

     //int ret = teacherDao.insertTeacherByParam("哈哈哥", 99);

     //log.info("影响的行数: " + ret);

     System.out.println( "--------------插入后:" );

     teacherList = teacherDao.queryAll();

     System.out.println(teacherList);

   }

   @Test

   public void testUpdate() {

     Teacher teacher = new Teacher();

     teacher.setId( 6 );

     teacher.setAge( 99 );

     teacher.setTname( "乔巴老师" );

     int rows = teacherDao.updateTeacherById(teacher);

     log.info( "更新行数:" + rows);

   }

   @Test

   public void testDelete() {

     int rows = teacherDao.deleteTeacherById( 13 );

     log.info( "删除行数:" + rows);

   }

   @Test

   public void testinsertTeacherByParam(){

int row=teacherDao.insertTeacherByParam( "hh" , 22 );

     System.out.println(row);

   }

}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。如有错误或未考虑完全的地方,望不吝赐教。

原文链接:https://blog.csdn.net/pan_junbiao/article/details/103349270

查看更多关于MyBatis中#{}占位符与${}拼接符的用法说明的详细内容...

  阅读:8次