在全局配置文件中,各个标签要按照如下顺序进行配置,因为mybatis加载配置文件的源码中是按照这个顺序进行解析的
<configuration>
<!-- 配置顺序如下
properties
settings
typeAliases
typeHandlers
objectFactory
plugins
environments
environment
transactionManager
dataSource
mappers
-->
</configuration>
例如以下配置文件
<?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="properties/db.properties"></properties>
<typeAliases>
<package name="com.yogurt.po"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${db.driver}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.user}"/>
<property name="password" value="${db.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper class="com.yogurt.mapper.PureStudentMapper"/>
</mappers>
</configuration>
<properties>
一般将数据源的信息单独放在一个properties文件中,然后用这个标签引入,在下面environment标签中,就可以用${}占位符快速获取数据源的信息
<settings>
用来开启或关闭mybatis的一些特性,或者添加一些功能,比如可以用来开启延迟加载,可以用来开启二级缓存
<typeAliases>
在mapper.xml中需要使用parameterType和resultType属性来配置SQL语句的输入参数类型和输出参数类型,类必须要写上全限定名,比如一个SQL的返回值映射为Student类,则resultType属性要写com.yogurt.po.Student,这太长了,所以可以用别名来简化书写,比如
<typeAliases>
<package name="com.yogurt.po"/>
</typeAliases>
如此,指定包下的所有类,都会以简单类名的小写形式,作为它的别名
另外,对于基本的Java类型 -> 8大基本类型以及包装类,以及String类型,mybatis提供了默认的别名,别名为其简单类名的小写,比如原本需要写java.lang.String,其实可以简写为string
<typeHandlers>
用于处理Java类型和Jdbc类型之间的转换,mybatis有许多内置的TypeHandler,比如StringTypeHandler,会处理Java类型String和Jdbc类型CHAR和VARCHAR。这个标签用的不多
<objectFactory>
mybatis会根据resultType或resultMap的属性来将查询得到的结果封装成对应的Java类,它有一个默认的DefaultObjectFactory,用于创建对象实例,这个标签用的也不多
<plugins>
可以用来配置mybatis的插件,比如在开发中经常需要对查询结果进行分页,就需要用到pageHelper分页插件,这些插件就是通过这个标签进行配置的。在mybatis底层,运用了责任链模式+动态代理去实现插件的功能
<!-- PageHelper 分页插件 -->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="helperDialect" value="mysql"/>
</plugin>
</plugins>
<environments>
用来配置数据源
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${db.driver}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.user}"/>
<property name="password" value="${db.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
用来配置mapper.xml映射文件,这些xml文件里都是SQL语句
<mappers>
<mapper class="com.yogurt.mapper.PureStudentMapper"/>
</mappers>
mapper.xml的SQL语句中的占位符${}
和#{}
一般会采用#{},#{}在mybatis中,最后会被解析为?,其实就是Jdbc的PreparedStatement中的?占位符,它有预编译的过程,会对输入参数进行类型解析(如果入参是String类型,设置参数时会自动加上引号),可以防止SQL注入,如果parameterType属性指定的入参类型是简单类型的话(简单类型指的是8种java原始类型再加一个String),#{}中的变量名可以任意,如果入参类型是pojo,比如是Student类
public class Student{
private String name;
private Integer age;
//setter/getter
}
那么#{name}表示取入参对象Student中的name属性,#{age}表示取age属性,这个过程是通过反射来做的,这不同于 , {}, ,{}取对象的属性使用的是OGNL(Object Graph Navigation Language)表达式
而${}
,一般会用在模糊查询的情景,比如SELECT * FROM student WHERE name like '%${name}%'
;
它的处理阶段在#{}
之前,它不会做参数类型解析,而仅仅是做了字符串的拼接,若入参的Student对象的name属性为zhangsan,则上面那条SQL最终被解析为SELECT * FROM student WHERE name like '%zhangsan%'
;
而如果此时用的是SELECT * FROM student WHERE name like '%#{name}%'
; 这条SQL最终就会变成
SELECT * FROM student WHERE name like '%'zhangsan'%'
; 所以模糊查询只能用${}
,虽然普通的入参也可以用${}
,但由于${}
不会做类型解析,就存在SQL注入的风险,比如
SELECT * FROM user WHERE name = '${name}' AND password = '${password}
’
我可以让一个user对象的password属性为’OR ‘1’ = '1,最终的SQL就变成了
SELECT * FROM user WHERE name = 'yogurt' AND password = ''OR '1' = '1'
,因为OR ‘1’ = '1’恒成立,这样攻击者在不需要知道用户名和密码的情况下,也能够完成登录验证
另外,对于pojo的入参,${}
中获取对象属性的语法和#{}
几乎一样,但${}在mybatis底层是通过OGNL表达式语言进行处理的,这跟#{}的反射处理有所不同
对于简单类型(8种java原始类型再加一个String)的入参,${}
中参数的名字必须是value,例子如下
<select id="fuzzyCount" parameterType="string" resultType="int">
SELECT count(1) FROM `user` WHERE name like '%${value}%'
</select>
为什么简单类型的变量名必须为value呢?因为mybatis源码中写死的value