MapperScan注解包含多个包
单个包
1
|
@MapperScan("com.mysiteforme.admin.dao")
|
多个包
1
|
@MapperScan({"com.mysiteforme.admin.dao","com.zipon.tpf.dao"})
|
实体类忽略映射字段
非数据库字段,如果不做处理,mybatis执行sql过程中会报错,Cause: java.sql.SQLSyntaxErrorException: Unknown column ‘…’ in ‘field list’
对非数据库字段使用注解@TableField(exist = false)
1
2
|
@TableField(exist = false)//:表示该属性不为数据库表字段,但又是必须使用的。
@TableField(exist = true)//:表示该属性为数据库表字段。
|
Mybatis-Plus 插件有这个功能,可以看一下
1
2
3
4
|
@TableName//:数据库表相关
@TableId//:表主键标识
@TableField//:表字段标识
@TableLogic//:表字段逻辑处理注解(逻辑删除)
|
script标签作用
1、用script
标签包围,然后像xml语法一样书写
1
2
3
4
5
6
7
|
@Select({"<script>",
"SELECT * FROM tbl_order",
"WHERE 1=1",
"<when test='title!=null'>",
"AND mydate = #{mydate}",
"</when>",
"</script>"})
|
2、用Provider
去实现SQL拼接,例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
public class OrderProvider {
private final String TBL_ORDER = "tbl_order";
public String queryOrderByParam(OrderPara param) {
SQL sql = new SQL().SELECT("*").FROM(TBL_ORDER);
String room = param.getRoom();
if (StringUtils.hasText(room)) {
sql.WHERE("room LIKE #{room}");
}
Date myDate = param.getMyDate();
if (myDate != null) {
sql.WHERE("mydate LIKE #{mydate}");
}
return sql.toString();
}
}
public interface OrderDAO {
@SelectProvider(type = OrderProvider.class, method = "queryOrderByParam")
List<Order> queryOrderByParam(OrderParam param);
}
|
注意:方式1有个隐患就是当传入参数为空的时候,可能会造成全表查询。
复杂SQL用方式2会比较灵活(当然,并不建议写复杂SQL),而且可以抽象成通用的基类,使每个DAO都可以通过这个基类实现基本的通用查询,原理类似Spring JDBC Template
。
3、说明:
如果XML元素嵌入在<script>
XML元素中,则可以在注释值中为动态SQL使用XML元素:
1
|
@Select("<script>SELECT ...</script>")
|
但是使用<include>
元素会触发SQL Mapper配置解析异常,由以下原因引起:
1
|
org.apache.ibatis.builder.BuilderException: Unknown element in SQL statement. at org.apache.ibatis.scripting.xmltags.XMLScriptBuilder.parseDynamicTags
|
如果nodeHandlers
在课堂中检查方法org.apache.ibatis.builder.BuilderException
,将注意到支持的元素有:
- trim
- where
- set
- foreach
- if
- choose
- when
- otherwise
- bind
然而,包括基于注释的查询中的片段是不可能的。
参考:
https://segmentfault.com/q/1010000006875476(以上内容转自此篇回答)
https://stackoverflow.com/questions/6685655/how-to-use-dynamic-sql-query-in-mybatis-with-annotationhow-to-use-selectprovide
http://www.blogjava.net/dbstar/archive/2011/08/08/355825.html
http://www.jianshu.com/p/03642b807688
https://stackoverflow.com/questions/33129344/is-there-a-way-to-reuse-sql-fragments-in-annotation-based-mybatis-ibatis
<![CDATA[ ]]>
的作用、使用、注意事项。
-
sql中字符串拼接
1
|
SELECT * FROM tableName WHERE name LIKE CONCAT(CONCAT('%', #{text}), '%');
|
-
使用 ${…} 代替 #{…} (不能防止sql注入 #{}—>可以防止sql注入的问题)
1
|
SELECT * FROM tableName WHERE name LIKE '%${text}%';
|
-
程序中拼接
Java
1
2
3
|
// or String searchText = "%" + text + "%";
String searchText = new StringBuilder("%").append(text).append("%").toString();
parameterMap.put("text", searchText);
|
SqlMap.xml
1
|
SELECT * FROM tableName WHERE name LIKE #{text};
|
<![CDATA[ ]]>
标记避免Sql中与xml规范相冲突的字符对xml映射文件的合法性造成影响
如:<![CDATA[price_from >= #{price_from}]]>
limit参数
参数page=1,rows=3
错误写法:
1
|
select * from xxx limit #{page},#{rows}
|
报错:
1
|
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''1','3'' at line 1
|
可以看出mybatis自动给我们的参数加了 引号 ' ‘,所以报错
解决方法:#{}改为${}
正确写法:
1
|
select * from xxx limit ${page},${rows}
|
结果封装为List
1.结果封装为List
接口示例:
1
2
|
接口示例:
public List<Members> selectMembersListByName(String name);
|
配置文件示例:
1
2
3
|
<select id="selectMembersListByName" resultType="members">
select * from members where member_name like #{member_name}
</select>
|
运行结果:
1
2
3
|
运行结果:
[Member [id=3, member_name=关云长, password=123456, age=54],
Member [id=4, member_name=关云长, password=123456, age=54]]
|
返回值类型resultType还是对象类型并不是list,mybatis会自动将对象封装成list集合
将单条记录封装成Map<String,Object>,其中key就是表的列名,value就是对应的值
接口示例:
1
|
public Map<String, Object> selectMembersByIdReturnMap(Integer id);
|
配置文件示例:
其中resultType是map,因为mybatis为常用类已经起了别名(typeAliases)
1
2
3
|
<select id="selectMembersByIdReturnMap" resultType="map">
select * from members where id = #{id}
</select>
|
运行结果:
1
|
{pass_word=123456, id=1, member_name=张三, age=25}
|
将多条记录封装成Map其中key是我们指定字段的名字,value是记录对象Map<String,Members>
接口示例:
@MapKey就是指定map中key是什么
1
2
|
@MapKey("id")
public Map<String, Members> selectMembersByNameReturnMap(String name);
|
配置文件示例:
返回值类型还是对象
1
2
3
|
<select id="selectMembersByNameReturnMap" resultType="members">
select * from members where member_name like #{member_name}
</select>
|
运行结果:
1
2
|
{3=Member [id=3, member_name=关云长, password=123456, age=54],
4=Member [id=4, member_name=关云长, password=123456, age=54]}
|
MyBatis动态传入表名
mybatis里#{}与${}的用法:
在动态sql解析过程,#{}与${}的效果是不一样的:
#{ } 解析为一个 JDBC 预编译语句(prepared statement)的参数标记符。
如以下sql语句
1
|
select * from user where name = #{name};
|
会被解析为:
1
|
select * from user where name = ?;
|
可以看到#{}被解析为一个参数占位符?。
${ } 仅仅为一个纯碎的 string 替换,在动态 SQL 解析阶段将会进行变量替换
如以下sql语句:
1
|
select * from user where name = ${name};
|
当我们传递参数“sprite”时,sql会解析为:
1
|
select * from user where name = "sprite";
|
可以看到预编译之前的sql语句已经不包含变量name了。
综上所得, ${ } 的变量的替换阶段是在动态 SQL 解析阶段,而 #{ }的变量的替换是在 DBMS 中。
#{}与${}的区别可以简单总结如下:
- #{}将传入的参数当成一个字符串,会给传入的参数加一个双引号
- ${}将传入的参数直接显示生成在sql中,不会添加引号
- #{}能够很大程度上防止sql注入,${}无法防止sql注入
${}在预编译之前已经被变量替换了,这会存在sql注入的风险。如下sql
1
|
select * from ${tableName} where name = ${name}
|
如果传入的参数tableName为user; delete user; –,那么sql动态解析之后,预编译之前的sql将变为:
1
|
select * from user; delete user; -- where name = ?;
|
–之后的语句将作为注释不起作用,顿时我和我的小伙伴惊呆了!!!看到没,本来的查询语句,竟然偷偷的包含了一个删除表数据的sql,是删除,删除,删除!!!重要的事情说三遍,可想而知,这个风险是有多大。
- ${}一般用于传输数据库的表名、字段名等
- 能用#{}的地方尽量别用${}
进入正题,通过上面的分析,相信大家可能已经对如何动态调用表名和字段名有些思路了。示例如下:
1
2
3
4
5
6
|
<select id="getUser" resultType="java.util.Map" parameterType="java.lang.String" statementType="STATEMENT">
select
${columns}
from ${tableName}
where COMPANY_REMARK = ${company}
</select>
|
要实现动态调用表名和字段名,就不能使用预编译了,需添加statementType=“STATEMENT”" 。
statementType:STATEMENT(非预编译),PREPARED(预编译)或CALLABLE中的任意一个,这就告诉 MyBatis 分别使用Statement,PreparedStatement或者CallableStatement。
默认:PREPARED。这里显然不能使用预编译,要改成非预编译。
其次,sql里的变量取值是${xxx},不是#{xxx}。
因为${}是将传入的参数直接显示生成sql,如${xxx}传入的参数为字符串数据,需在参数传入前加上引号,如:
1
2
|
String name = "sprite";
name = "'" + name + "'";
|
@ Transient注解
ORM框架中是否和数据库的列做映射,帮助理解XmlTransient
Mybatis传递多个参数的4种方式
现在大多项目都是使用Mybatis了,但也有些公司使用Hibernate。使用Mybatis最大的特性就是sql需要自己写,而写sql就需要传递多个参数。面对各种复杂的业务场景,传递参数也是一种学问。
下面给大家总结了以下几种多参数传递的方法。
方法1:顺序传参法
1
|
public User selectUser(String name, int deptId);
|
1
2
3
4
|
<select id="selectUser" resultMap="UserResultMap">
select * from user
where user_name = #{0} and dept_id = #{1}
</select>
|
#{}
里面的数字代表你传入参数的顺序。
这种方法不建议使用,sql层表达不直观,且一旦顺序调整容易出错。
方法2:@Param
注解传参法
1
|
public User selectUser(@Param("userName") String name, int @Param("deptId") deptId);
|
1
2
3
4
|
<select id="selectUser" resultMap="UserResultMap">
select * from user
where user_name = #{userName} and dept_id = #{deptId}
</select>
|
#{}
里面的名称对应的是注解 @Param
括号里面修饰的名称。
这种方法在参数不多的情况还是比较直观的,推荐使用。
方法3:Map传参法
1
|
public User selectUser(Map<String, Object> params);
|
1
2
3
4
|
<select id="selectUser" parameterType="java.util.Map" resultMap="UserResultMap">
select * from user
where user_name = #{userName} and dept_id = #{deptId}
</select>
|
#{}
里面的名称对应的是 Map里面的key名称。
这种方法适合传递多个参数,且参数易变能灵活传递的情况。
方法4:Java Bean传参法
1
|
public User selectUser(User params);
|
1
2
3
4
|
<select id="selectUser" parameterType="com.test.User" resultMap="UserResultMap">
select * from user
where user_name = #{userName} and dept_id = #{deptId}
</select>
|
#{}
里面的名称对应的是 User类里面的成员属性。
这种方法很直观,但需要建一个实体类,扩展不容易,需要加属性,看情况使用。
Mybatis使用in传入List的三种方法
1.非xml方式,使用注解传in,要使用
1
2
3
4
5
6
7
8
|
@Select("<script>" +
"SELECT count(DISTINCT member_id) as memberCount\n" +
"from member_analysis\n" +
"WHERE agent_id in <foreach item='item' index='index' collection='memberIds' open='(' separator=',' close=')'>" +
"#{item}" +
"</foreach>" +
"</script>")
Integer getCountWithAgentId(@Param("memberIds") List<String> memberIds);
|
其中的foreach的collection直接写成@param中的值即可。
2.在入参前进行字符串封装,拼成(,,,,)传值
1
2
|
@Select("SELECT count(DISTINCT member_id) as memberCount from member_analysis where access_pat_id in (#{memberIds})")
Integer getCountWithAgentId(@Param("memberIds")String memberIds);
|
使用方法返回
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public static String indexForm(List<String> s){
String content="";
int i=0;
for (String ss:s){
i++;
if (i==s.size()){
content+=ss;
}else {
content+=ss+",";
}
}
return content;
}
|
或者使用
1
|
StringUtils.join(memberIds.toArray(),",")
|
3.正常流程的xml方法
1
2
3
4
5
6
7
8
9
|
<select id="findByIds" resultMap="BaseResultMap">
select * from dictionaries
<where>
dictionaries.key in
<foreach item="item" index="index" collection="memberTypes" open="(" separator="," close=" )">
#{item}
</foreach>
</where>
</select>
|
type-aliases-package配置多个
问题
现在扫描的是com.ruoyi.project.domain
这个路径,我想扫描多个路径,如:com.test.project.domain
这个路径,该如何配置?
我试了加逗号及空格都不行
typeAliasesPackage: com.ruoyi.project..domain,com.test.project..domain
解决
typeAliasesPackage: com.ruoyi.project..domain,;com.test.project..domain
,使用,;
详细讲解可以参考:type-aliases-package配置多个