全局配置文件的属性
properties属性(基本不用)
<!-- mybatis可以使用properties来引入外部properties配置文件的内容
resource:引入类路径下的资源
url:引入网络路径下的资源,或者磁盘上绝对路径
-->
<properties resource="jdbcconfig.properties"></properties>
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT%2B8
jdbc.username=root
jdbc.password=1234
settings设置
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。
比如里面的mapUnderscoreToCamelCase就是是否开启自动驼峰命名规则(camel case)映射,就是从数据库列名THE_NAME到java属性名theName的类映射,使用查询的时候能自动进行匹配,注意下划线后面的字母还是大写的,这是驼峰命名规则
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
typeAliases别名处理器
<!--
typeAliases:别名处理器,可以为我们的Java类型起别名(别名不区分大小写)
typeAlias是为某一个Java类型起别名
type:指定要起别名的类型全限类名,默认是类名的小写
alias:指定新的别名
package:为某个包下的所有类批量起别名
name:指定包名(为当前包以及下面的所有子包的每一个类都起一个默认别名)
但是上面的还有一个问题,就是这个包和它的子包有一个类的名字是一样的
这样的话,mybatis就会报错,所以我们还有一个方法起别名
使用@Alias注解起别名
批量起别名的情况下,使用注解为某个类型指定新的别名
-->
<typeAliases>
<typeAlias type="bean.Emp" alias="emp"/>
<package name=""/>
</typeAliases>
environments属性
<!--
environments:多种环境,mybatis可以配置多种环境,default是指定使用某种环境,达到快速切换环境的效果
environment:配置一个具体的环境信息,id是这个环境的唯一标识
transactionManager:事务管理器
type:事务管理器的类型,有:
JDBC:JdbcTransactionFactory
MANAGED:ManagedTransactionFactory
还可以自定义事务管理器:只要实现TransactionFactory接口,type是指定它的全限类名就行,
或者参考上面两个这么写的就怎么写
dataSource:数据源
type:数据源的类型,有;
JNDI:JndiDataSourceFactory
POOLED:PooledDataSourceFactory
UNPOOLED:UnpooledDataSourceFactory
还可以自定义数据源:实现DataSourceFactory接口就行,type就是它的全限类名
-->
<environments default="development_mysql">
<environment id="development_mysql">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</dataSource>
</environment>
</environments>
databaseIdProvider属性
全局配置
<!--
databaseIdProvider:让mybatis支持多数据库厂商,让mybatis的移植性更好
type="DB_VENDOR":作用是得到数据库厂商的标识(就是驱动,根据getDatabaseProductName()的方法得到)
mybatis就能根据数据库厂商标识来执行不同的sql
比如MySQL Oracle SQL Server
可以利用下面这个标签给数据库厂商起一个好用的别名
<property name="" value=""/>
然后就是映射文件的配置,
在select标签里面的databaseId="mysql"属性表明这个查询是使用哪一个数据库的
-->
<databaseIdProvider type="DB_VENDOR">
<property name="MySQL" value="mysql"/>
<property name="Oracle" value="oracl"/>
</databaseIdProvider>
映射文件配置
如果这里是在mysql环境下,会加载有mysql标识的和没有任何标识的语句,如果执行的时候优先使用带mysql标识的sql
<mapper namespace="bean.EmpMapper">
<select id="getEmpById" resultType="bean.Emp" >
select eid,ename name,email,gender from emp where eid= #{id}
</select>
<!-- 这是两个版本数据库的查询,上面是mysql,下面的是oracle的查询 -->
<select id="getEmpById" resultType="bean.Emp" databaseId="mysql">
select eid,ename name,email,gender from emp where eid= #{id}
</select>
<select id="getEmpById" resultType="bean.Emp" databaseId="oracle">
select eid,ename name,email,gender from emp where eid= #{id}
</select>
</mapper>
这里也配置了两个环境
<environments default="development_mysql">
<environment id="development_mysql">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</dataSource>
</environment>
<environment id="development_oracle">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${orcl.driver}" />
<property name="url" value="${orcl.url}" />
<property name="username" value="${orcl.username}" />
<property name="password" value="${orcl.password}" />
</dataSource>
</environment>
</environments>
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT%2B8
jdbc.username=root
jdbc.password=1234
orcl.driver=oracle.jdbc.OracleDriver
orcl.url=jdbc:oracle:thin:@localhost:1521:orcl
orcl.username=scott
orcl.password=1234
mappers映射注册
<!-- 将写好的sql映射文件注册到全局配置文件中 -->
<!--
mappers:将sql映射注册到全局文件中
mapper:注册一个sql映射
注册配置文件
resource:引用类路径下的sql映射文件
url:引用网络路径或者磁盘路径的sql映射文件
注册接口:
class:引用接口,就是注册接口,而不是映射文件了
1.有sql映射文件的,映射文件名必须和接口同名,而且是要放在接口同一目录下
2.mybatis也支持没有sql映射文件的,就是所有的sql都是写在接口的注解上面
上面两种方法的推荐:
比较重要的,复杂的Dao接口写在映射文件上
不重要,简单的Dao接口为了开发方便可以写在注解上面
3.第三种就是批量注册了,使用package标签
注意的是批量注册会找到包下的所有类
如果是接口注释的我们还能理解,
但是如果是文件注册的mybatis是怎么找到映射文件的呢?
所以这个批量注册还是有要求的,如果有映射注册,
就必须接口类和映射文件在同一个包下
注意你可能觉得java源文件和xml文件放在一起,这样不好看
所以你可以在资源文件夹conf的目录下新建一个和接口类一模一样的包名
那么在编译的时候,因为src和conf都是资源文件夹,所以会被解析到一个文件夹里面
那么这两个文件夹就是同一个文件夹了,只是在开发的时候视觉上不是同一个文件夹
-->
<mappers>
<mapper resource="EmpMapper.xml" />
<mapper class="bean.EmpMapperAnnotation"/>
<package name="bean"/>
</mappers>
注意那些文件路径的问题, 比如什么java源文件,批量注册的包路径名,包之间使用.(点号), 而什么xml文件的路径名,包之间使用的是/(斜杠)
映射文件
增删改查
下面的代码中,增删改的操作做,需要返回值就直接是返回Integer/Long/Boolean这些类型,这些返回值mybatis已经定义好了,会自动帮你封装,只要返回不是0行数据,Boolean都是True
package bean;
public interface EmpMapper
{
public Emp getEmpById(Integer id);
public long addEmp(Emp emp);
public boolean updateEmp(Emp emp);
public long deleteEmpById(Integer eid);
}
映射文件写法
<!--
public long addEmp(Emp emp);
public boolean updateEmp(Emp emp);
public long deleteEmpById(Integer eid);
-->
<insert id="addEmp" parameterType="bean.Emp">
insert into emp (ename,email,gender) values(#{ename},#{email},#{gender})
</insert>
<update id="updateEmp">
update emp set ename=#{ename},email=#{email},gender=#{gender}
where eid=#{eid}
</update>
<delete id="deleteEmpById">
delete from emp where eid=#{eid}
</delete>
@org.junit.Test
public void Test4() throws IOException
{
String resource="mybatis.xml";
InputStream resourceAsStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession openSession = sqlSessionFactory.openSession();
try
{
EmpMapper empMapper = openSession.getMapper(EmpMapper.class);
//Emp emp=new Emp(2, "jane", "jane.com", "男");
//empMapper.addEmp(emp);
//boolean updateEmp = empMapper.updateEmp(emp);
//System.out.println(updateEmp);
long deleteEmpById = empMapper.deleteEmpById(2);
System.out.println(deleteEmpById);
//必须手动提交
openSession.commit();
}finally
{
openSession.close();
}
}
映射文件的insert获取主键的值
和JDBC一样,mybatis也是通过使用statement.getGenreatedKeys()来获取,需要设置两个参数
useGeneratedKeys=“true” keyProperty=“eid”
keyProperty="eid"是将返回的主键赋值给哪一个变量,将这个返回的值封装给JavaBean的哪一个属性
<insert id="addEmp" parameterType="bean.Emp" useGeneratedKeys="true" keyProperty="eid">
insert into emp (ename,email,gender) values(#{ename},#{email},#{gender})
</insert>
EmpMapper empMapper = openSession.getMapper(EmpMapper.class);
Emp emp=new Emp(null, "jane", "jane.com", "男");
empMapper.addEmp(emp);
System.out.println(emp.getEid());
Oracle的处理方法
<!--
获取非自增的主键的值:
像Oracle是不支持自增的,Oracle使用的是序列来模拟自增的
每一次插入的数据的主键都是从序列中拿到的值,那么我们如何获取这个值呢?
mybatis支持使用属性selectKey来获取主键:
keyProperty:查出的主键值封装给JavaBean的哪一个属性
order:设置当前的selectKey的sql语句在插入sql语句的前后运行,有两个参数
BEFORE:当前的selectKey的sql语句在插入sql语句的前运行
运行顺序:
先运行selectKey查询id的sql;查出id值封装给javaBean的id属性
在运行插入的sql;就可以取出id属性对应的值
AFTER:当前的selectKey的sql语句在插入sql语句的后运行
运行顺序:
先运行插入的sql(从序列中取出新值作为id);
再运行selectKey查询id的sql;
resultType:查出的数据的返回值类型
-->
<!-- BEFORE的编写 -->
<insert id="addEmp" parameterType="bean.Emp" databaseId="oracle">
<selectKey keyProperty="eid" order="BEFORE" resultType="Integer">
select EMPLOYEES_SEQ.nextval from dual
</selectKey>
<!-- 插入时的主键时从序列中拿到的 -->
insert into emp (eid,ename,email,gender) values(#{eid},#{ename},#{email},#{gender})
</insert>
<!-- AFTER的编写 -->
<insert id="addEmp" parameterType="bean.Emp" databaseId="oracle">
<selectKey keyProperty="eid" order="AFTER" resultType="Integer">
select EMPLOYEES_SEQ.currval from dual
</selectKey>
<!-- 插入时的主键时从序列中拿到的 -->
insert into emp (eid,ename,email,gender) values(employees_seq.nextval,#{ename},#{email},#{gender})
</insert>
映射文件的参数问题
<!--
mybatis的参数问题:
单个参数:mybatis不会做特殊的处理
#{参数名/任意的名称} :都可以取出参数的值
多个参数:mybatis会做特殊的处理
多个参数会被封装成一个map对象,
key:名称就是param1,param2...paramN,或者参数的索引也可以取出值
value:就是你传入的参数的值
如果直接写参数名,
select eid,ename,email,gender from emp where eid= #{eid} and ename=#{ename}
会报错:
Cause: org.apache.ibatis.binding.BindingException:
Parameter 'eid' not found.
Available parameters are [0, 1, param1, param2]
我们可以将参数写成0,1或者param1,param2
但是如果参数很多,就显得不合规矩
所以我们可以命名参数:
明确指定封装参数时map的key的值是什么,
使用的是@Param注释进行指明
key:就是使用@Param的值
value:参数值
例如:public Emp getEmpByIdAndName(@Param("eid")Integer eid,@Param("ename")String ename);
上面的命名参数,如果参数实在是不少,那么写起来还是挺麻烦的,下面还有三种方法
POJO:
如果多个参数正好是我们业务逻辑的数据模型,那么我们就可以直接传入pojo,就是JavaBean类
使用:#{属性名}进行取出传入的pojo的属性值
Map:
如果多个参数不是业务逻辑的数据模型,没有对应的pojo,不经常使用这个sql,为了方便
我们可以自己构建一个map传进去
使用:#{key}就可以取出map对应的值
TO:
如果多个参数不是业务模型的数据,但是经常使用,推荐自己编写一个TO
(Transfer Object)数据传输对象
例子:
public Emp getEmp(@Param("eid")Integer eid,String ename)
取值:eid==>#{eid}或#{param1}
ename==>#{param2}
public Emp getEmp(Integer eid,@Param("e")Emp emp)
取值:eid==>#{param1}
ename==>#{param2.ename}或者#{e.ename}
##注意的是:如果是Collection(List,Set)类型或者是数组
mybatis也会特殊处理,把传入的list或者数组封装在map中
key:Collection封装成collection
如果是List可以使用list
数组封装成array
public Emp getEmpById(List<Integer> eids)
取值:取出第一个id的值:#{list[0]}
结合源码进行理解:
参数多时会封装map,为了不混乱,我们可以使用@Param来指定封装时使用的key
使用#{key}就可以取到map中的值
例子:
(@Param("eid")Integer eid,@Param("ename")String ename);
ParamNameResolver解析参数封装map的;
1. 首先names:{0=eid,1=ename};这是在构造器的时候就创建好了
怎么创建的呢:
1.获取每一个标注了@Param注解的参数的@Param的值eid,ename,赋值给names
2.每一次解析一个参数给map中保存信息
(key:参数索引,就是第几个参数,value:name的值
name的值:标注了@Param注解的:就是注解的值
没有标注的:
1.全局配置:isUseActualParamName (jdk1.8):name=参数名
2.name=map.size();就是当前元素的索引值
比如{0=eid,1=ename,2=2}//如果有第三个元素的话
2.后面就是对参数args的处理
假设现在args=[7,"jane"]
1.如果参数时null就直接返回
2.如果只有一个元素,并且没有@Param注解,直接返回args[0]
3.有多个元素或者有@Param注解
遍历names集合{0=eid,1=ename,2=2}
names集合的value作为key,names集合的key又作为取值的参数args[entry.getKey()
由上面的例子得到:
{eid=args[0],ename=args[1],2=args[2]}
结果是:{eid=7,ename="jane",2=2}
4.最后还额外将每一个参数保存在map中,使用新的key:param1,param2...paramN
这样就可以:有param注解的可以#{key}取值,或者#{param1}取值
public Object getNamedParams(Object[] args)
{
final int paramCount = names.size();
if (args == null || paramCount == 0)
{
return null;
}
else if (!hasParamAnnotation && paramCount == 1)
{
return args[names.firstKey()];
}
else
{
final Map<String, Object> param = new ParamMap<Object>();
int i = 0;
for (Map.Entry<Integer, String> entry : names.entrySet())
{
param.put(entry.getValue(), args[entry.getKey()]);
// add generic param names (param1, param2, ...)
final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
// ensure not to overwrite parameter named with @Param
if (!names.containsValue(genericParamName))
{
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
return param;
}
//names的源码
private final SortedMap<Integer, String> names;
private boolean hasParamAnnotation;
public ParamNameResolver(Configuration config, Method method)
{
final Class<?>[] paramTypes = method.getParameterTypes();
final Annotation[][] paramAnnotations = method.getParameterAnnotations();
final SortedMap<Integer, String> map = new TreeMap<Integer, String>();
int paramCount = paramAnnotations.length;
// get names from @Param annotations
for (int paramIndex = 0; paramIndex < paramCount; paramIndex++)
{
if (isSpecialParameter(paramTypes[paramIndex]))
{
// skip special parameters
continue;
}
String name = null;
for (Annotation annotation : paramAnnotations[paramIndex])
{
if (annotation instanceof Param)
{
hasParamAnnotation = true;
name = ((Param) annotation).value();
break;
}
}
if (name == null)
{
// @Param was not specified.
if (config.isUseActualParamName())
{
name = getActualParamName(method, paramIndex);
}
if (name == null)
{
// use the parameter index as the name ("0", "1", ...)
// gcode issue #71
name = String.valueOf(map.size());
}
}
map.put(paramIndex, name);
}
names = Collections.unmodifiableSortedMap(map);
}
-->
package bean;
import org.apache.ibatis.annotations.Param;
public interface EmpMapper
{
public Emp getEmpByIdAndName(@Param("eid")Integer eid,@Param("ename")String ename);
public Emp getEmpByEmp(Emp emp);
public Emp getEmpById(Integer id);
public long addEmp(Emp emp);
public boolean updateEmp(Emp emp);
public long deleteEmpById(Integer eid);
}
参数值的获取
<!--
参数值的获取:
#{}:可以获取map中的值或者pojo对象的属性的值
${};效果和上面的一样
区别是:
#{}是以预编译的形式将参数设置到sql语句中的,使用的方法是PreparedStatement,可以防止sql注入
${}是将取出的值直接拼接在sql语句中的,会有安全问题
大多数情况,我们使用的是#{}
但是原生JDBC不支持占位符的地方,我们就可以使用${}进行取值
比如:分表,排序,模糊查询....
按照年份查询薪资
select * from ${year}_salary where xxxx;
select * from emp orde by ${f_name} ${order}
select * from emp where ename like '%${thechar}%'
-->
#{}的其他用法
<!--
#{}的其他用法:
规定参数的一些规则:
javaType、 jdbcType、 mode(存储过程)、 numericScale、
resultMap、 typeHandler、 jdbcTypeName、 expression(未来准备支持的功能);
jdbcType通常在某种特定的条件下需要设置:
就是我们需要插入的数据的某个字段是null值的时候
有些数据库可能不能识别mybatis对null值得默认处理,
比如Oracle就无法识别,插入的时候会报错,JdbcType OTHER:无效的类型;
因为mybatis得全局配置中,对于所有的null值都是默认映射原生得jdbc得OTHER类型
Oracle无法识别这种类型
解决的方法有:
原因是全局配置的jdbcTypeForNull=OTHER,
1.#{email,jdbcType=OTHER}
2.将全局配置改变:
<setting name="jdbcTypeForNull" value="NULL"/>
-->