1、关于#{}占位符
先来看以下的示例,该示例是MyBatis中的SQL映射配置文件(Mapper配置文件),在该配置中使用了#{}占位符。
INSERT INTO tb_user(user_name,blog_url,remark) VALUES(#{userName},#{blogUrl},#{remark});
在SQL映射配置文章中,输入参数需要用占位符来标识对应的位置。
在传统的JDBC编程中,占位符用“?”来标识,然后在加载SQL之前按照“?”的位置设置参数。
而“#{}”在MyBatis中也代表一种占位符,该符号接受输入参数,在大括号中编写参数名称来接受对应参数。
“#{}”接受的输入参数的类型可以是简单类型、普通JavaBean或者HashMap。
当接受简单类型时,“#{}”中可以写“value”或者其他任意名称。
如果接受的是JavaBean,它会通过OGNL读取对象中的属性值。
2、关于${}拼接符
再来看以下的示例,该示例是MyBatis中的SQL映射配置文件(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注释掉,表示这两个属性项未进行配置。
jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/db_admin?useSSL=false& #jdbc.username=root #jdbc.password=123456
(2)启用拼接符默认值的配置
要使用“${}”拼接符中设置一个默认值,需要在properties标签中设置启用拼接符默认值的配置项。
在MyBatis配置文件(mybatis-config.xml)中,使用
注意:在MyBatis配置文件(mybatis-config.xml)中,
必须按照:
(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类
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; } }
接口类
package com.lanou3g.mybaties.dao; import com.lanou3g.mybaties.bean.Teacher; import org.apache.ibatis.annotations.Param; import java.util.List; public interface TeacherDao { ListqueryAll(); 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配置文件
TeacherMapper.xml映射文件
insert into teacher(tname) values (#{tname}); insert into teacher(tname, age) values (#{tname}, #{age}); update teacher set tname = #{tname}, age = #{age} where id = #{id} delete from teacher where id = #{id};
前面完成后,就要测试了。不过在测试时需要实例化创建对象,因要实现多个方法,在这先建MyBatisTools.java工具类,其主要进行封装Mybatis初始化操作,要求支持创建多env sqlSessionFactory,整个应用生命周期内相同env的sqlSessionFactory对象只有一个(这个类不要过于探究,会用即可)。
MyBatisTools.java
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 ConcurrentHashMapfactoryMap = 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
@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(){ Listteachers=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 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); } }
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。如有错误或未考虑完全的地方,望不吝赐教。