学向勤中得,萤窗万卷书。这篇文章主要讲述java mybatis学习之$和#区别,mapper代理接口,动态SQL,在日志中输出mybatis的sql语句相关的知识,希望能为你提供帮助。
1.在mybatis中,$和#的区别:
#{}:表示一个预处理参数,参数类型不定,是根据传入的参数类型来设定的。类似于JDBC中的?
特例使用,模糊查询:(针对oracle):
and username like concat(concat(‘%‘,#{username}),‘%‘)
${}:相当于是我们的JDBC里的字符串拼接。这里就相当于传入的就是一个字符串(不管传入什么样的数据类型,都是字符串)
and username like ‘%${value}%‘
2.$和#在mybatis中的优缺点:
#{}:传进去的值可以设置其数据类型。会根据传入的数据类型自动加字符串的单引号或者不加单引号。预处理参数。可以防止SQL注入。
${}:采取的$的方式传入参数,所有采取$的方式传入的参数都只是字符串(无论传入的是什么,都会当成字符串处理),潜在的危险就是SQL注入的问题。而${}的优势在于字符串的拼接可以处理sql语句的一些特殊情况,例如:
默认情况下,使用#{}格式的语法会导致 MyBatis 创建预处理语句属性并以它为背景设 置安全的值(比如?) 。这样做很安全,很迅速也是首选做法,有时你只是想直接在 SQL 语 句中插入一个不改变的字符串。比如,像 ORDER BY,你可以这样来使用:
ORDER BY ${columnName}
3.mapper代理接口和动态SQL
直接上代码:
这是测试的代码
1 package com.jinglin.hotelsup.test; 2 3 import java.io.IOException; 4 import java.io.InputStream; 5 import java.util.List; 6 7 import org.apache.ibatis.io.Resources; 8 import org.apache.ibatis.session.SqlSession; 9 import org.apache.ibatis.session.SqlSessionFactory; 10 import org.apache.ibatis.session.SqlSessionFactoryBuilder; 11 import org.apache.log4j.Logger; 12 import org.junit.Test; 13 14 import com.jinglin.hotelsup.dao.impl.UserInfoDao; 15 import com.jinglin.hotelsup.mapper.UserInfoMapper; 16 import com.jinglin.hotelsup.model.UserInfo; 17 18 public class TestUserInfo { 19 20//日志 21static Logger logger = Logger.getLogger(TestUserInfo.class); 22//定义一个工厂 23static SqlSessionFactory factory = null; 24//静态块 25static{ 26InputStream inputStream = null; 27try { 28inputStream =Resources.getResourceAsStream("mybatis-config.xml"); 29} catch (IOException e) { 30// TODO Auto-generated catch block 31e.printStackTrace(); 32logger.error("创建连接对象出错,错误信息:"+e.getMessage()); 33} 34factory = new SqlSessionFactoryBuilder().build(inputStream); 35} 36 37@Test 38public void test(){ 39SqlSession session = factory.openSession(); 40//代理的接口 41UserInfoMapper userInfoMapper = session.getMapper(UserInfoMapper.class); 42//添加 43/*UserInfo userInfo = new UserInfo(); 44userInfo.setUsername("张三3号"); 45//userInfo.setUserpwd("123456"); 46userInfo.setJob("程序员"); 47//userInfo.setCard("521354499653159894"); 48int count = userInfoMapper.insertItem(userInfo); 49//事务提交 50session.commit(); 51//关闭 52session.close(); 53System.out.println("受影响行数:"+count); 54System.out.println("刚刚插入的主键:"+userInfo.getUserid()); */ 55 56//修改 57/*UserInfo userInfo = new UserInfo(); 58userInfo.setUsername("张三1号"); 59userInfo.setUserpwd("123456"); 60 //userInfo.setJob("程序员"); 61 //userInfo.setCard("521354499653159894"); 62userInfo.setUserid(21); 63int count = userInfoMapper.updateItem(userInfo); 64//事务提交 65session.commit(); 66//关闭 67session.close(); 68System.out.println("受影响行数:"+count); */ 69 70//逻辑删除 71/*UserInfo userInfo = new UserInfo(); 72userInfo.setUserid(21); 73int count = userInfoMapper.deleteItem(userInfo); 74//事务提交 75session.commit(); 76//关闭 77session.close(); 78System.out.println("受影响行数:"+count); */ 79 80//查询多条 81/*UserInfo userInfo = new UserInfo(); 82//userInfo.setUsername("张三"); 83List< UserInfo> list = userInfoMapper.selectAll(userInfo); 84for (UserInfo u : list) { 85System.out.println(u.getUsername()+"\\t"+u.getUserpwd()); 86}*/ 87 88//模糊查询 89/*UserInfo userInfo = new UserInfo(); 90userInfo.setUsername("张"); 91List< UserInfo> list = userInfoMapper.getDimList(userInfo); 92for (UserInfo u : list) { 93System.out.println(u.getUsername()+"\\t"+u.getUserpwd()); 94}*/ 95} 96 }
mybatis-config配置
< ?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> < settings> < !-- 通过日志记录显示mybatis的执行过程 --> < setting name="logImpl" value="https://www.songbingjia.com/android/LOG4J"/> < /settings> < !-- 定义别名 --> < typeAliases> < typeAlias type="com.jinglin.hotelsup.model.UserInfo" alias="UserInfo"/> < /typeAliases> < !-- 定义多个配置环境 --> < environments default="development"> < !-- 定义其中一个配置环境 --> < environment id="development"> < !-- 定义事务处理类型 --> < transactionManager type="JDBC"/> < !-- 定义数据源 --> < dataSource type="POOLED"> < !-- 数据库驱动名称 --> < property name="driver" value="https://www.songbingjia.com/android/oracle.jdbc.driver.OracleDriver"/> < !-- 数据库URL地址 --> < property name="url" value="https://www.songbingjia.com/android/jdbc:oracle:thin:@localhost:1521:orcl"/> < !-- 数据账号 --> < property name="username" value="https://www.songbingjia.com/android/hotelmanager"/> < !-- 数据库密码 --> < property name="password" value="https://www.songbingjia.com/android/123456"/> < /dataSource> < /environment> < /environments> < mappers> < mapper resource="com/jinglin/hotelsup/mapper/UserInfoMapper.xml"/> < /mappers> < /configuration>
dao层的接口
package com.jinglin.hotelsup.dao; import java.util.List; public interface IDaoHotelsup< T> { public int insertItem(T t); //添加一条数据 public int updateItem(T t); //修改一条数据 public T selectById(T t); //根据id查询单条 public int deleteItem(T t); //删除一条数据 public List< T> selectAll(T t); //查询全部 public List< T> getDimList(T t); //模糊查询 }
mapper接口,在这里是接口继承了接口
package com.jinglin.hotelsup.mapper; import com.jinglin.hotelsup.dao.IDaoHotelsup; import com.jinglin.hotelsup.model.UserInfo; public interface UserInfoMapper extends IDaoHotelsup< UserInfo> {}
mapper的映射
< ?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="com.jinglin.hotelsup.mapper.UserInfoMapper"> < !-- 定义重复使用的代码段 --> < sql id="selectColumns"> select * from userinfo where del=‘N‘ < /sql> < !-- 添加 --> < insert id="insertItem" parameterType="UserInfo" useGeneratedKeys="true" keyColumn="userid" keyProperty="userid"> insert into userinfo < trim prefix="(userid," suffixOverrides=","> < if test="username != null"> username, < /if> < if test="userpwd != null"> userpwd, < /if> < if test="card != null"> card, < /if> < if test="job != null"> job < /if> < /trim> < trim prefix=")values(usersid.nextval,"> < if test="username != null"> #{username}, < /if> < if test="userpwd != null"> #{userpwd}, < /if> < if test="card != null"> #{card}, < /if> < if test="job != null"> #{job} < /if> < /trim> ) < /insert> < !-- 修改 --> < update id="updateItem" parameterType="UserInfo"> update userinfo < set> < if test="username != null"> username=#{username}, < /if> < if test="userpwd != null"> userpwd=#{userpwd}, < /if> < if test="card != null"> card=#{card}, < /if> < if test="job != null"> job=#{job} < /if> < /set> where userid=#{userid} < /update> < !-- 删除 --> < delete id="deleteItem" parameterType="UserInfo"> < !-- delete from userinfo where userid=#{userid} --> < !-- 逻辑删除 --> update userinfo set del=‘Y‘ where userid=#{userid} < /delete> < !-- 根据id查询 --> < select id="selectById" resultType="UserInfo" parameterType="UserInfo"> < include refid="selectColumns"> < /include> and userid=#{userid} < /select> < !-- 查询全部 --> < select id="selectAll" resultType="UserInfo"> < include refid="selectColumns"> < /include> < if test="username != null and username != ‘‘"> and username=#{username} < /if> < if test="userpwd != null and userpwd != ‘‘"> and userpwd=#{userpwd} < /if> < if test="card != null and card != ‘‘"> and card=#{card} < /if> < if test="job != null and job != ‘‘"> and job=#{job} < /if> < /select> < !-- 迷糊查询--> < select id="getDimList" resultType="UserInfo" parameterType="UserInfo"> < include refid="selectColumns"> < /include> < if test="username != null and username != ‘‘"> and username like concat(concat(‘%‘,#{username}),‘%‘) < /if> < if test="userpwd != null and userpwd != ‘‘"> and userpwd like concat(concat(‘%‘,#{userpwd}),‘%‘) < /if> < if test="card != null and card != ‘‘"> and card like concat(concat(‘%‘,#{card}),‘%‘) < /if> < if test="job != null and job != ‘‘"> and job like concat(concat(‘%‘,#{job}),‘%‘) < /if> < /select> < /mapper>
以下是日志文件显示SQL语句
首先,注意mybatis-config中的log4j的< setting> 标签
< settings> < !-- 通过日志记录显示mybatis的执行过程 --> < setting name="logImpl" value="https://www.songbingjia.com/android/LOG4J"/> < /settings>
其次,是log4j部分
#将ibatis log4j运行级别调到DEBUG可以在控制台打印出ibatis运行的sql语句 log4j.rootLogger=debug,stdout,logfile ### 把日志信息输出到控制台 ### log4j.appender.stdout=org.apache.log4j.ConsoleAppender #log4j.appender.stdout.Target=System.err log4j.appender.stdout.layout=org.apache.log4j.SimpleLayout ### 把日志信息输出到文件:jbit.log ### log4j.appender.logfile=org.apache.log4j.FileAppender log4j.appender.logfile.File=F:/logs/test.log log4j.appender.logfile.layout=org.apache.log4j.PatternLayout log4j.appender.logfile.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %F %p %m%n ###显示SQL语句部分 log4j.logger.com.ibatis=DEBUG log4j.logger.com.ibatis.common.jdbc.SimpleDataSource=DEBUG log4j.logger.com.ibatis.common.jdbc.ScriptRunner=DEBUG log4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=DEBUG log4j.logger.java.sql.Connection=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.PreparedStatement=DEBUG
显示SQL语句部分则是实现将sql语句显示出来的关键,实现结果如下
文章图片
接下来是动态SQL的详解,
动态SQL,顾名思义就是将SQL语句动态化,区别与传统的SQL语句。动态SQL语句的优势在于它的灵活,在开发项目的过程中可以减少代码量
【java mybatis学习之$和#区别,mapper代理接口,动态SQL,在日志中输出mybatis的sql语句】MyBatis 的一个强大的特性之一通常是它的动态 SQL 能力。 如果你有使用 JDBC 或其他 相似框架的经验,你就明白条件地串联 SQL 字符串在一起是多么的痛苦,确保不能忘了空 格或在列表的最后省略逗号。动态 SQL 可以彻底处理这种痛苦。
if
在动态 SQL 中所做的最通用的事情是包含部分 where 字句的条件。比如:
< select id="findActiveBlogWithTitleLike" parameterType="Blog" resultType="Blog"> SELECT * FROM BLOG WHERE state = ‘ACTIVE’ < if test="title != null"> AND title like #{title} < /if> < /select>
这条语句会提供一个可选的文本查找功能。如果你没有传递 title,那么所有激活的博客 都会被返回。但是如果你传递了 title,那么就会查找相近的 title(对于敏锐的检索,这中情 况下你的参数值需要包含任意的遮掩或通配符)的博客。
假若我们想可选地搜索 title 和 author 呢?首先,要改变语句的名称让它有意义。然后 简单加入另外的一个条件。
< select id="findActiveBlogLike" parameterType="Blog" resultType="Blog"> SELECT * FROM BLOG WHERE state = ‘ACTIVE’ < if test="title != null"> AND title like #{title} < /if> < if test="author != null and author.name != null"> AND author_name like #{author.name} < /if> < /select>
choose, when, otherwise
有时我们不想应用所有的条件, 相反我们想选择很多情况下的一种。 Java 中的 switch 和 语句相似,MyBatis 提供 choose 元素。
我们使用上面的示例,但是现在我们来搜索当 title 提供时仅有 title 条件,当 author 提 供时仅有 author 条件。如果二者都没提供,只返回 featured blogs(也许是由管理员策略地选 择的结果列表,而不是返回大量没有意义的随机博客结果列表)。
< select id="findActiveBlogLike" parameterType="Blog" resultType="Blog"> SELECT * FROM BLOG WHERE state = ‘ACTIVE’ < choose> < when test="title != null"> AND title like #{title} < /when> < when test="author != null and author.name != null"> AND author_name like #{author.name} < /when> < otherwise> AND featured = 1 < /otherwise> < /choose> < /select>
trim, where, set
前面的例子已经方便地处理了一个臭名昭著的动态 SQL 问题。要考虑我们回到“if”示 例后会发生什么,但是这次我们将“ACTIVE = 1”也设置成动态的条件。
< select id="findActiveBlogLike" parameterType="Blog" resultType="Blog"> SELECT * FROM BLOG WHERE < if test="state != null"> state = #{state} < /if> < if test="title != null"> AND title like #{title} < /if> < if test="author != null and author.name != null"> AND author_name like #{author.name} < /if> < /select>
如果这些条件都没有匹配上将会发生什么?这条 SQL 结束时就会成这样:
SELECT * FROM BLOG WHERE
这会导致查询失败。如果仅仅第二个条件匹配是什么样的?这条 SQL 结束时就会是这 样:
SELECT * FROM BLOG WHERE AND title like ‘someTitle’
这个查询也会失败。这个问题不能简单的用条件来解决,如果你从来没有这样写过,那 么你以后也不会这样来写。
MyBatis 有一个简单的处理,这在 90%的情况下都会有用。而在不能使用的地方,你可 以自定义处理方式。加上一个简单的改变,所有事情都会顺利进行:
< select id="findActiveBlogLike" parameterType="Blog" resultType="Blog"> SELECT * FROM BLOG < where> < if test="state != null"> state = #{state} < /if> < if test="title != null"> AND title like #{title} < /if> < if test="author != null and author.name != null"> AND author_name like #{author.name} < /if> < /where> < /select>
where 元素知道如果由被包含的标记返回任意内容,就仅仅插入“WHERE” 。而且,如 果以“AND”或“OR”开头的内容,那么就会跳过 WHERE 不插入。
如果 where 元素没有做出你想要的,你可以使用 trim 元素来自定义。比如,和 where 元素相等的 trim 元素是:
< trim prefix="WHERE" prefixOverrides="AND |OR "> ... < /trim>
prefixOverrides 属性采用管道文本分隔符来覆盖, 这里的空白也是重要的。 它的结果就是移除 在 prefixOverrides 属性中指定的内容,插入在 with 属性中的内容。
和动态更新语句相似的解决方案是 set。set 元素可以被用于动态包含更新的列,而不包 含不需更新的。比如:
< update id="updateAuthorIfNecessary" parameterType="domain.blog.Author"> update Author < set> < if test="username != null"> username=#{username},< /if> < if test="password != null"> password=#{password},< /if> < if test="email != null"> email=#{email},< /if> < if test="bio != null"> bio=#{bio}< /if> < /set> where id=#{id} < /update>
这里,set 元素会动态前置 SET 关键字,而且也会消除任意无关的逗号,那也许在应用 条件之后来跟踪定义的值。
如果你对和这相等的 trim 元素好奇,它看起来就是这样的:
< trim prefix="SET" suffixOverrides=","> ... < /trim>
注意这种情况下我们覆盖一个后缀,而同时也附加前缀。
推荐阅读
- android之使用GridView+仿微信图片上传功能(附源代码)
- 使用Tomcat服务器在Eclipse IDE中创建JSP
- 如何修复Windows 10中的intelppm.sys蓝屏错误(分步指南)
- 如何修复错误代码0x80070052(解决办法分步指南)
- 如何修复Windows 10中的XBOX错误代码0x80070005(解决办法)
- 如何修复Java错误代码1618(解决办法介绍)
- 如何修复Xbox One错误代码0x8000ffff(解决办法指南)
- Android Studio入门到精通
- Android中关闭DatePickerTimePickerNumberPicker的可编辑模式