06-Mybatis中的传参、属性和动态SQL 1、mybatis中参数传递
1.1 MyBatis中对sql语句参数的传递
这⾥要明⽩⼀点,由于mybatis是在dao层起作⽤,dao层的接⼝中定义了和⼀个sql映射⽂件中各条sql语句的⽅法,
就是说接⼝中的⽅法中需要传递的参数都是与sql语句中需要的参数对应的,
那么映射⽂件中每个sql语句中添加parameterType属性以指定传递参数是什么类型就变得可有可⽆了。
1、--⼀个参数的传递
这类情况就是mapper⽂件中的sql语句只需要⼀个参数,如按表中id查询数据。
传递⼀个参数⼀般都是传递简单数据类型,dao接⼝中的⽅法⼀般如下:
public User selectUserById(Integer id);
映射⽂件中的sql语句的占位符中变量名可以随便写:select * from t_user where id=#{随便写}
当我们⽤接⼝实现类对象调⽤selectUserById时,动态代理⽣成的实现类中调⽤SqlSession对象的selectOne⽅法,
将整型参数传进去,因为sql语句中只需要⼀个参数,这个值怎么传都会传给sql中的参数位置,
所以在占位符中特意指定接收属性的参数值。
2、--多参数传递
这类情况就是mapper⽂件中的sql语句需要多个参数,
如按表中id和(或)name查询数据,或者是insert操作,亦或是update操作等等。
多参传递有多种⽅式:
--1)注解@Param命名参数传参--(掌握)
@Param("参数名") 数据类型变量名
dao接⼝⽅法中参数定义如下:
public List<User> selectByNameOrId(@Param("loginName") String name,@Param("id") Integer id);
当调⽤该⽅法时内部是将我们输⼊的name(id)参数的值以注解中定义的参数名为key存储到⼀个集合中(这只是猜测),
所以sql语句中的占位符中填写正确的对应参数名#{}
--当然sql中的条件⼀般都是表中的字段,所以参数名都是按表中对应字段命名。
--2)JavaBean对象传参
dao接⼝⽅法中参数定义如下:
public List<User> addUser(User user);
例如作添加数据操作:
需要创建JavaBean对象,然后给该对象相关属性赋值,最后作为参数传递给到、接⼝的⽅法中
映射⽂件中接收参数的占位符中填参数名就是User对象中各个属性名,为了开发⽅便,
就得使这个实体类中JavaBean的字段名和表中的字段名⼀致
--3)按位置传参
dao接⼝⽅法中参数根据前后进⾏排序,第⼀个是0,第⼆个是1.
直接调⽤dao接⼝⽅法时将参数写到⽅法中,就完成传参操作了。
这种⽅式不需要我们定义0位置参数的名字,mybatis已经定义好了。mybatis3.3版本以及之前版本是0,代表第⼀个
3.4及以后⽤arg0表⽰第⼀个参数。很明显mybatis把接⼝⽅法中的参数放进了⼀个可变长度的参数数组,⽽sql语句中
的占位符可以根据下标0、1、2...来接收参数,只不过下标前⾯加了arg。
select * from t_user where id=#{arg0} or loginName=#{arg1}
只不过这种⽅式不知道具体arg0代表的是什么意思的值,容易搞错。
--4)使⽤集合Map传值
public List<User> selectByMap(Map<String,Object>);
⾸先得创建Map集合,往集合中存储数据put("表中字段名",value),集合key命名为对应表中的字段名,
调⽤dao接⼝⽅法时把Map集合传进去。
sql语句中的占位符中填写的是map集合中的key,所以key的命名与表中⼀致可以很便捷。
--这四种⽅法⽤的最多的是前两种⽅法,在需要传递的参数涉及两张表时,⾸选@Param,其次是Map⽅式
1.2 占位符#和$的区别
mybatis映射⽂件中sql语句参数使⽤"#{参数名}"来代替JDBC中的"?"占位符,其实mybatis还有⼀种占位符是"${参数名}"
我们知道,JDBC有两种获取数据库操作对象的⽅式:
"PrepareStatement"和"Statement"
前者是将sql语句先编译,没有值的位置⽤"?"这个占位符来代替,后期通过
PrepareStatement对象的setInt(整数值)(或setString(字符串)等等)⽅法给"?"赋值,然后执⾏sql语句。
后者则是让sql语句进⾏按需求进⾏添加字符串的拼接,然后执⾏sql语句,有sql注⼊风险。
⽽mapper⽂件中sql语句⾥的占位符⽤#{}的话,底层执⾏时,使⽤的是PrepareStatement对象进⾏的,
所以这个#{}是mybatis⽤来代替?的。
如果mapper⽂件中sql语句⾥的占位符⽤${},$表⽰告诉mybatis使⽤$表⽰的字符串替换这条sql语句其所在位置。
主要⽤来替换表名列名,不同序列等操作。
1.3 测试根据id查询t_user表中的数据,通过输出执⾏⽇志来⽐较使⽤#或者是$占位符的区别
select * from t_user where id=#{id}
⽇志输出如下:
Opening JDBC Connection
Created connection 230643635.
Setting autocommit to false on JDBC Connection [sql.jdbc.JDBC4Connection@dbf57b3]
==> Preparing: select * from t_user where id=?
==> Parameters: 1(Integer)
<== Columns: id, loginName, loginPwd, realName
<== Row: 1, xxpiaozhiyan, yanyan, 朴智妍
<== Total: 1
User{id=1, loginName='xxpiaozhiyan', loginPwd='yanyan', realName='朴智妍'}
--所以⽤#得到的是:select * from t_user where id=? 然后给?赋值
select * from t_user where id=${id}
⽇志输出如下:
Opening JDBC Connection
Created connection 1388278453.
Setting autocommit to false on JDBC Connection [sql.jdbc.JDBC4Connection@52bf72b5]
==> Preparing: select * from t_user where id=1
==> Parameters:
<== Columns: id, loginName, loginPwd, realName
<== Row: 1, xxpiaozhiyan, yanyan, 朴智妍
<== Total: 1
User{id=1, loginName='xxpiaozhiyan', loginPwd='yanyan', realName='朴智妍'}
--所以⽤$得到的是:select * from t_user where id=1 然后直接运⾏
2、mybatis中resultMap属性
2.1 补充前⾯对结果集的处理
前⾯学习过使⽤JavaBean对象来存储查询结果集,这是针对查询⼀张表的情况,在resultType中填写JavaBean的完整类名。
--这种⽅式还有⼀个要求就是:
查询结果的列名要和JavaBean对象的属性名⼀致。要保持这种⼀致性有两种⽅法:
1)javabean类的字段名与要查询的表中的字段名全部⼀致
2)在查询语句中将查询结果的列名起别名,该别名和JavaBean的字段名⼀致,达到传递数据结果到JavaBean对象的要求
--除了以上以外,如果JavaBean类中的字段名与表中的不⼀致,结果集列名⼜不起别名的情况下:
使⽤<resultMap>标签进⾏"结果映射",--指定结果集列名和java对象属性的对应关系
该标签是与sql语句标签平级的,在<select>等标签之前设置,有⾃⼰的唯⼀标识id,⽽且设置对应关系时列名如果是主键的值与其他普通列名的指定是由区别的。案例如下:
2.2 使⽤resultMap属性实现结果映射
我的实体类如下User.java,⼀定要确保实体类中有⽆参数的构造⽅法,因为mybatis的动态代理是⽤实体类的⽆参构造来⾃动创建实体类对象的
batis.domain;
public class User {
private int id;
private String account;
private String password;
private String name;
public User() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", account='" + account + '\'' +
", password='" + password + '\'' +
", name='" + name + '\'' +
'}';
}
}
sql映射⽂件如下
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-////DTD Mapper 3.0//EN"
"/dtd/mybatis-3-mapper.dtd">
<mapper namespace="test">
<!--
注意:parameterType属性是给sql语句中占位符传值的,定义占位符传什么类型对象的属性的值到该位置上
翻译为:参数类型,下⾯insert语句中传递的是batis.domain.User类的属性字段值
同时javabean 在给占位符传值时,程序员要告诉mybatis 应该将javabean 的哪个属性字段值传到哪个占位符上
所以占位符中需要有javabean 的属性字段名
mybatis 中占位符不⽤问号表⽰,因为要放javabean 的字段信息,所以⽤:#{这⾥写javabean 的属性字段名} 表⽰
#{loginName}底层原理是mybatis 会将#{}中的属性字段名获取到,将其⾸字母⼤写,然后在前⾯添加get ,调⽤
javabean 中的各字段的get ⽅法来获取到值,从⽽组成sql 语句执⾏
-->
<!--定义resultMap
id ⾃⼰定义,代表你定义的这个resultMap
type :填对应Java 类型的全限定名--> <resultMap id="user" type="batis.domain.User">
<!--对主键结果列映射的设置
使⽤id 标签
column :填查询结果的列名
property :Java 类型的属性名
-->
<id column="id" property="id"/>
<!--⾮主键列
使⽤result 标签--> <result column="loginName" property="account"/>
<result column="loginPwd" property="password"/>
<result column="realName" property="name"/>
</resultMap>
<!--使⽤上⾯设置的映射关系,直接将某个resultMap 的id 放进resultType 属性中-->
<select id="getById" resultType="user">
sql语句替换表中内容select
id,loginName,loginPwd,realName from
t_user
where id = #{id} </select> <select id="getAll" resultType="user">
select
* from
t_user
</select>
</mapper>
这种⽅式主要是将来不得不改表中的字段名,由于项⽬中字段名⼀般都是⾮常多的,修改java 类⿇烦,⽽sql 语句起别名也⿇烦,因为sql 语句可能有很多,这样我们就可以使⽤resultMap 属性设置映射关系。
--⼀般情况不⽤这种
--动态sql :
就是说sql 语句是动态变化的,映射⽂件中的sql 语句根据条件让mybatis 获取不同的sql 语句
主要是where 的部分进⾏动态化
--实现动态sql
通过使⽤mybatis 中的标签来实现sql 语句的动态化,标签有<if>、<where>、<foreach>
语法如下:
<if test="判断java 对象的属性值">
待拼接的sql 语句
</if>
在idea-maven-mybatis ⼯程中添加新模块进⾏测试,模块项⽬名为:d-maven-dynamic-sql-mybatis04
<!--注意标签<if>的动态sql 语句中接⼝⽅法中的参数⼀般是Java 对象-->
package com.studymyself.dao;
import ity.User;
import java.util.List;
//接⼝操作t_user 表
public interface UserDao {
//通过if 动态sql 语句查询数据,参数必须是Java 对象
public List<User> selectByIf(User user);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-////DTD Mapper 3.0//EN"
"/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.studymyself.dao.UserDao">
<select id="selectByIf" resultType="User">
select id,loginName,loginPwd,realName from t_user
3、mybatis 中的动态SQL
3.1 动态sql 的概念
3.2 标签,是判断条件的
3.3 测试测试标签标签
UserDao 接⼝⽅法如下:
sql 映射⽂件如下:
where
<!--上⾯表⽰主语句-->
<!--if 表⽰这个标签中传递过来的Java 对象的id 字段值⼤于0
就将if 标签中的sql 语句块拼接到主语句中去-->
<if test="id > 0">
id=#{id}
</if>
<!--传过来的java 对象中的realName 值不为空或不是空字符串
就将if 标签中的sql 语句块拼接到主语句中去-->
<if test="loginName!= null or loginName !='' ">
or realName = #{realName} </if>
</select>
</mapper>
@Test
public void testSelectByIf(){
//获取SqlSession 对象
SqlSession sqlSession = SqlSession();
//通过SqlSession 对象中的⽅法获取实现类对象
UserDao userDao = Mapper(UserDao.class);
User user = new User();
user.setId(1);
user.setRealName("钟荣杰");
List<User> users = userDao.selectByIf(user);
for (User user1:
users) {
System.out.println(user1); } }
从上⾯的语句中我们可以知道有这样的情况:
第⼀个if 标签中条件不成⽴,但第⼆个条件成⽴将sql 语句块添加到主语句时会拼接成错误的sql 语句块:
select id,loginName,loginPwd,realName from t_user where or realName = ?
避免这种情况,我们可以在where 后添加⼀个 1=1 的条件,然后第⼀个if 标签中的语句块前⾯都加连接关键字or and <select id="selectByIf" resultType="User">
select id,loginName,loginPwd,realName from t_user where 1=1
<if test="id > 0">
or id=#{id}
</if>
<if test="realName!= null or realName !='' "> or realName = #{realName}
</if>
</select>
<where>标签⽤来代替sql 主语句中where 关键的位置,⽤来包含多个if 标签,当多个if 只要有⼀个成⽴,该标签就⾃动⽣成⼀个where 关键字,还会将第⼀个拼接到sql 主语句的if 标签的sql 语句块的or 、and 连接关键字去掉。
package com.studymyself.dao;import ity.User;import java.util.List;
//接⼝操作t_user 表
public interface UserDao {
//通过if 动态sql 语句查询数据,参数必须是Java 对象
public List<User> selectByIf(User user);
//通过if 和where 动态sql 语句查询数据,参数必须是Java 对象
public List<User> selectByWhere(User user);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-////DTD Mapper 3.0//EN"
"/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.studymyself.dao.UserDao">
<select id="selectByIf" resultType="User">
select id,loginName,loginPwd,realName from t_user
where
<!--上⾯表⽰主语句-->
<!--if 表⽰这个标签中传递过来的Java 对象的id 字段值⼤于0
测试程序如下
上⾯在where 后边添加恒等条件的⽅式可以⽤标签解决
3.3 标签
3.4 测试测试标签标签
在dao 接⼝中添加⽅法,如下
sql 映射⽂件如下
就将if标签中的sql语句块拼接到主语句末端中去-->
<if test="id > 0">
id=#{id}
</if>
<!--传过来的java对象中的realName值不为空或不是空字符串
就将if标签中的sql语句块拼接到主语句末端中去-->
<if test="realName!= null or realName !='' ">
or realName = #{realName}
</if>
</select>
<select id="selectByWhere" resultType="User">
select id,loginName,loginPwd,realName from t_user
<where>
<if test="id > 0">
or id=#{id}
</if>
<if test="realName!= null or realName !='' ">
or realName = #{realName}
</if>
</where>
</select>
</mapper>
测试程序如下
@Test
public void testSelectByWhere(){
/
/获取SqlSession对象
SqlSession sqlSession = SqlSession();
//通过SqlSession对象中的⽅法获取实现类对象
UserDao userDao = Mapper(UserDao.class);
User user = new User();
user.setId(1);
user.setRealName("钟荣杰");
List<User> users = userDao.selectByWhere(user);
for (User user1:
users) {
System.out.println(user1);
}
}
上⾯测试程序的执⾏⽇志如下
Opening JDBC Connection
Created connection 2114444063.
Setting autocommit to false on JDBC Connection [sql.jdbc.JDBC4Connection@7e07db1f]
==> Preparing: select id,loginName,loginPwd,realName from t_user WHERE id=? or realName = ?
==> Parameters: 1(Integer), 钟荣杰(String)
<== Columns: id, loginName, loginPwd, realName
<== Row: 1, xxpiaozhiyan, yanyan, 朴智妍
<== Row: 3, 16020520009, 123456789, 钟荣杰
<== Total: 2
User{id=1, loginName='xxpiaozhiyan', loginPwd='yanyan', realName='朴智妍'}
User{id=3, loginName='16020520009', loginPwd='123456789', realName='钟荣杰'}
从⽇志可以看到,条件where是个⼤写的⽣成的,还把or去掉了,两个if语句不成⽴的话就是⼀个⽆条件的查询语句。
3.5 标签
当我们要执⾏的是带有in条件的sql语句时,in中的值是多个的,
--当接⼝⽅法中传的参数是⼀个Map集合,我们可以⽤平常使⽤的⽅法把需要传参⽤#{}来代替,其中括号中的属性名是集合的key值,创建map集合,给集合添加数据,然后调⽤接⼝⽅法把map集合传进去。 --当接⼝中的参数是List集合时,我们创建List集合,给集合添加数据,然后调⽤接⼝⽅法把List集合传进去,由于List集合不是键值对存储的,传进去后没法通过特定属性名⽤#{}取值。
上⾯的第⼆种⽅式在⽤<foreach>标签后可以将List集合中的值存放到in条件中,原理如下⾯代码所⽰:
//下⾯代码演⽰<foreach>对传⼊的List集合处理的内部机理
//这⾥新建List集合
List<Integer> list = new ArrayList<>();
//添加数据
list.add(1);
list.add(4);
list.add(7);
//假设映射⽂件中sql主语句,需要添加in后⾯的条件
String sql = "select id,loginName,loginPwd,realName from t_user where id in";
//需要的条件是这样的:(1,7,4)
//创建⼀个字符串拼接类StringBuffer
StringBuffer buffer = new StringBuffer();
//添加字符(
buffer.append("(");
//遍历list集合,并且追加到buffer中,中间记住加,
for (Integer i:
list) {
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论