Mybatis两大主要文件详细解析
上篇文章已经了解到了mybatis的各文件的关系及合作,篇主要来详细解析mybatis的两个主要xml文件。
mybatis主要核心配置文件
在这我的mybatis核心文件就是mybatis-config.xml,工具类里创建连接工厂SqlSessionFactory的就是用这文件里的配置信息。先看下官方文档的核心文件简单内容。
<?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">
<!-- http://mybatis.org/dtd/mybatis-3-config.dtd 是这个核心文件的规范,这个xml文件里的标签得按照它里面的格式 -->
<configuration>
<!-- configuration :整个文件的配置都在这个标签内 -->
<environments default="development">
<!-- environments :所有的环境都得在这个标签内
default : 当前mybatis正在使用的环境
-->
<environment id="development">
<!-- 单个环境得配置 id :环境的唯一标识 -->
<!-- 以下便是使用的环境的配置 -->
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- 全部的mapper文件的位置配置 -->
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
<!-- mapper : 配置单个sql映射的文件,可以直接用包路 径配置
resource :文件路径,有多种书写方式,下面例举
-->
</mappers>
</configuration>
数据库连接配置
关于数据库连接的配置信息想必大家都以了解,不过多赘述
<transactionManager type="JDBC"/>
<!-- transactionManager:事务管理器
属性: type 表示事务管理器的类型。
属性值:1)JDBC: 使用Connection对象, 由mybatis自己完成事务的处理。
2) MANAGED: 管理,表示把事务的处理交给容器实现(由其他软件完成事务的提交,回滚) -->
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<!--driver:驱动的内容-->
<property name="url" value="${url}"/>
<!--连接数据库的url-->
<property name="username" value="${username}"/>
<!--用户名-->
<property name="password" value="${password}"/>
<!--密码-->
</dataSource>
dataSource: 数据源,创建的Connection对象,连接数据库。
属性: type 数据源的类型
属性值:1) POOLED, mybatis会在内存中创建PooledDataSource类,管理多个Connection连接对象,使 用的连接池
2) UNPOOLED ,不使用连接池, mybatis创建一个UnPooledDataSource这个类, 每次执行sql 语句先创建Connection对象,再执行sql语句,最后关闭Connection
3) JNDI : java的命名和目录服务。
mapper 标签
使用mapper指定其他mapper文件的位置,
mapper标签使用的格式有两个常用的方式:
<mappers>
<!--第一种方式, resources="mapper文件的路径"
优点:文件清晰。 加载的文件是明确的。
文件的位置比较灵活。
缺点:文件比较多, 代码量会比较大, 管理难度大
-->
<!-- 使用相对于类路径的资源引用 -->
<mapper resource="com/bjpowernode/dao/StudentDao.xml"/>
<!-- 使用完全限定资源定位符(URL) -->
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
<!--
使用映射器接口实现类的完全限定类名
需要配置文件名称和接口名称一致,并且位于同一目录下
-->
<mapper resource="com/bjpowernode/dao/StudentDao"/>
<!--
第二种方式,使用<package>
name:包名, mapper文件所在的包名。
特点: 把这个包中的所有mapper文件,一次加载。
使用要求:
1. mapper文件和dao接口在同一目录
2. mapper文件和dao接口名称完全一样。
-->
<package name="com.bjpowernode.dao" />
<package name="com.bjpowernode.dao1" />
</mappers>
根据核心文件的规范文件可以看出除了这些配置还有一些经常要用到的比较实用的配置
configuration(配置)
properties(属性)
settings(设置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境配置)
environment(环境变量)
transactionManager(事务管理器)
dataSource(数据源)
databaseIdProvider(数据库厂商标识)
mappers(映射器)
properties(属性)
导入外部配置文件
<properties resource="jdbc.properties">
<!--<property name="键" value="值"/>-->
<!-- 设置键值-->
</properties>
properties内的键值是另外设置属性的方式,(不建议)xml文件中另外设置的键值优先级低于在导入的文件中设置的。
使用键值 : “${键}”
settings(设置)
settings是mybatis的全局设置,影响整个mybatis的运行。 这个设置一般使用默认值就可以了。一般只设置日志格式。
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="false"/>
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25"/>
<setting name="defaultFetchSize" value="100"/>
<setting name="safeRowBoundsEnabled" value="false"/>
<setting name="mapUnderscoreToCamelCase" value="false"/>
<setting name="localCacheScope" value="SESSION"/>
<setting name="jdbcTypeForNull" value="OTHER"/>
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>
typeAliases(别名)
别名设置、别名作用于mapper文件中的类型填写。对于那些经常要用到的类可以这样设置,方便操作。
<typeAliases>
<!--第一种语法格式
type:java类型的全限定名称(自定义类型)
alias:自定义别名
优点: 别名可以自定义
缺点: 每个类型必须单独定义
-->
<typeAlias type="com.bjpowernode.domain.Student" alias="stu" />
<typeAlias type="类的全限定名" alias="别名" />
<!--第二种方式
name:包名, mybatis会把这个包中所有类名作为别名(不用区分大小写)
优点:使用方便,一次给多个类定义别名
缺点: 别名不能自定义,必须是类名。
-->
<package name="com.bjpowernode.domain" />
<package name="com.bjpowernode.vo" />
</typeAliases>
以下方法与方法二相同
<typeHandlers>-->
<!-- <package name="类位置" />-->
<!--默认别名为类名(类名小写),如果类中有注解则使用注解的别名(@Alias("别名"))-->
</typeHandlers>
objectFactory(对象工厂)
-
MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。
-
默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过有参构造方法来实例化。
-
如果想覆盖对象工厂的默认行为,则可以通过创建自己的对象工厂来实现。
plugins(插件)
<plugins>
<!-- 配置使用分页插件 -->
<plugin interceptor ="com.github.pagehelper.PageInterceptor" />
</plugins>
Mapper(sql文件)
Mapper文件是mybatis操作数据库的关键文件,里面主要设置操作数据库的命令语句,惯例先来看下mybatis官方文档的举例
<?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="org.mybatis.example.BlogMapper">
<select id="selectBlog" resultType="Blog">
select * from Blog where id = #{id}
</select>
</mapper>
http://mybatis.org/dtd/mybatis-3-mapper.dtd :约束文件,规定了 mapper文件的格式规范。规范如下:
mapper : 根标签
namespace :命名空间,必须有值,不能为空,唯一值。
一般使用对应Dao接口的位置名称。mybatis会操作这个文件的信息和映射对应接口的信息
进行对数据库的操作。
<select id="selectBlog" resultType="Blog">
select * from Blog where id = #{id}
</select>
不同的操作命令对应着不同的标签,如上查询数据库数据的标签为,那就可以知道规范文件里,,这些标签的作用了。不同的操作标签要对应着相同的sql操作命令,例如你的操作是查询某个表的数据,sql语句为select * from table;则标签必须为。
添加操作示范
<insert id="insertuser" parameterType="user">
insert into user value(${id},"${name}",${man})
</insert>
增、删、改都操作其实都差不多的,懂得举一反三就好。
标签属性:
id :对应着Dao接口相关操作方法的名称
resultType:返回值类型,如果接口方法的返回值是集合或数组则这里应该是集合中存储的类型或数组类型。mybatis是通过接口中方法的返回值判断是查询一条数据还是多条数据。总的来说mybatis底层还是基于jdbc。
#{id} : 占位符,Java程序中传过来的数据,如果传来的(对应方法中的参数)是基本类型则占位符中可以是任意字符。#{任意字符}
这里有Java程序传过来的数据,标签内应该还有一个属性parameterType,官方文档中省略不写。
parameterType :Java程序传过来数据的类型(可以不写),mybatis通过反射机制可以获取。
参数解析(*)
占位符里内容要看Java程序中传来参数而定
单参数:占位符中可以是任意字符
#{任意字符}
多参数:占位符中得写上@Param注释设置的名称
例:
dao接口,方法的定义
/*
多个简单类型的参数
使用@Param命名参数, 注解是mybatis提供的
位置:在形参定义的前面
属性:value 自定义的参数名称
*/
List<Student> selectByNameOrAge(@Param("myname") String name,
@Param("myage") Integer age);
mapper文件
<!--
多个简单类型的参数.
当使用了@Param命名后,例如@Param("myname").
在mapper中,使用#{命名的参数}, 例如 #{myname}
-->
<select id="selectByNameOrAge" resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student where name=#{myname} or age=#{myage}
</select>
对象参数:占位符中应该是对应的属性
dao接口方法使用一个对象作为参数
方法的形参是一个java对象。这个java对象表示多个参数。使用对象的属性值作为参数使用
java对象
public class Student {
private Integer id;
private String name;
private String email;
private Integer age;
//set|get方法
}
public class QueryParam {
private Object p1;
private Object p2;
//set|get方法
}
dao接口中的方法定义
/*
* 一个java对象作为参数( 对象由属性, 每个属性有set,get方法)
*/
List<Student> selectByObject(Student student);
List<Student> selectByQueryParam(QueryParam param);
mapper文件
<!--
一个java对象作为方法的参数,使用对象的属性作为参数值使用
简单的语法: #{属性名} , mybatis调用此属性的getXXX()方法获取属性值
-->
<select id="selectByObject" resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student where name=#{name} or age=#{age}
</select>
<select id="selectByQueryParam" resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student where name=#{p1} or age=#{p2}
</select>
<!--负责的语法格式: #{属性名,javaType=java类型的全限定名称,jdbcType=mybatis中定义列的数据类型}-->
<select id="selectByObject" resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student where
name=#{name,javaType=java.lang.String,jdbcType=VARCHAR}
or
age=#{age,javaType=java.lang.Integer,jdbcType=INTEGER}
</select>
Map参数:占位符中填上对应的键
map作为dao接口的参数, 使用 key 获取参数值,mapper文件中,语法格式 #{key}
例:
/*
使用Map作为参数
*/
List<Student> selectStudentByMap(Map<String,Object> map);
mapper文件
<!--
使用Map传递参数,
在mapper文件中,获取map的值,是通过key获取的,语法:#{key}
-->
<select id="selectStudentByMap" resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student where name=#{myname} or age=#{myage}
</select>
还有一个是传来多基本类型参数的用位置取值法,这方法不推荐使用,在这就不过多讲解。
#和$的区别
# 占位符
语法: #{字符}
mybatis处理#{} 使用jdbc对象是 PrepareStatment对象
<select id="selectById" parameterType="integer"
resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student where id=#{studentId}
</select>
mybatis出创建PrepareStatement对象,执行sql语句
String sql=" select id,name,email,age from student where id=?";
PrepareStatement pst = conn.prepareStatement(sql);
pst.setInt(1,1001); //传递参数
ResultSet rs = pst.executeQuery(); //执行sql语句
#{}特点:
1)使用的PrepareStatement对象,执行sql语句,效率高。
2)使用的PrepareStatement对象,能避免sql语句, sql语句执行更安全。
3) #{} 常常作为 列值使用的, 位于等号的右侧, #{}位置的值和数据类型有关的。
$ 占位符
语法 : ${字符}
mybatis执行${}占位符的sql语句
<select id="selectById" parameterType="integer"
resultType="com.bjpowernode.domain.Student">
select id,name,email,age from student where id=${studentId}
</select>
${} 表示字符串连接, 把sql语句的其他内容和 ${}内容使用 字符串(+) 连接的方式连在一起
String sql="select id,name,email,age from student where id=" + "1001";
mybatis创建Statement对象, 执行sql语句。
Statement stmt = conn.createStatement(sql);
ResultSet rs = stmt.executeQuery();
${} 的特点
1)使用Statement对象,执行sql语句,效率低
2)${}占位符的值,使用的字符串连接方式, 有sql注入的风险。 有代码安全的问题
- ${} 数据是原样使用的, 不会区分数据类型。
4)${} 常用作 表名或者列名, 在能保证数据安全的情况下使用 ${}
别名使用
mapper文件中别名的使用是为了简化类的全限定名称的书写
用法:
在核心文件中设置好别名
<typeAliases>
<!--
type:java类型的全限定名称(自定义类型)
alias:自定义别名
-->
<typeAlias type="com.bjpowernode.domain.Student" alias="stu" />
</typeAliases>
mapper文件中使用
resultType="别名"
<select id="selectById" parameterType="integer" resultType="stu">
select id,name,email,age from student where id=#{studentId}
</select>
sql列名和java对象属性名称不一样解决方式
1) 使用resultMap: 自定义列名和属性名称对应关系
2)使用resultType: 使用列别名,让别名和java对象属性名称一样
这里说的别名与上面说的配mybatis类别名不一样,这里说的别名是sql中的特性,作用是更改操作结果的列名。用sql命令语句实现。
动态sql
什么是动态sql: 同一个dao的方法, 根据不同的条件可以表示不同的sql语句, 主要是where部分有变化
使用mybatis提供的标签,实现动态sql的能力, 主要讲 if ,where ,foreach, sql。
使用动态sql的时候, dao方法的形参使用java对象。
if 标签(不推荐使用)
语法:
<if test="boolean判断结果">
sql 代码
</if>
在mapper文件中
<select id="selectStudent" resultType="com.bjpwoernode.domain.Student">
select *from student
<if test="条件">
sql语句
</if>
<if test="条件">
sql语句
</if>
</select>
例子:
List<Student> selectIf(Student student);
<!--if
test: 使用对象的属性值作为条件
-->
<select id="selectIf" resultType="com.bjpowernode.domain.Student">
select * from student
where id=-1
<if test="name !=null and name!=''">
or name = #{name}
</if>
<if test="age >0">
or age < #{age}
</if>
</select>
where 标签(建议使用)
使用if标签时,容易引起sql语句语法错误。 使用where标签解决if产生的语法问题。
使用时 where ,里面是一个或多个if 标签, 当有一个if标签 判断条件为true, where标签会转为 WHERE 关键字附加到sql语句的后面。 如果 if 没有一个条件为true , 忽略where和里面的if。
where标签删除 和他最近的or 或者 and。
语法:
<where>
<if test="条件1">sql语句1</if>
<if test="条件2">sql语句2</if>
</where>
例子:
//where
List<Student> selectWhere(Student student);
<!--where-->
<select id="selectWhere" resultType="com.bjpowernode.domain.Student">
select * from student
<where>
<if test="name !=null and name!=''">
or name = #{name}
</if>
<if test="age >0">
or age < #{age}
</if>
</where>
</select>
foreach 循环
使用foreach可以循环数组,list集合, 一般使用在in语句中。
语法:
< foreach collection="集合类型" open="开始的字符" close="结束的字符"
item="集合中的成员" separator="集合成员之间的分隔符">
#{item 的值}
</ foreach>
标签属性:
collection: 表示,循环的对象是 数组, 还是list集合。 如果dao接口方法的形参是 数组,
collection="array" ,如果dao接口形参是List, collection="list"
open:循环开始时的字符。 sql.append("(");
close:循环结束时字符。 sql.append(")");
item:集合成员, 自定义的变量。 Integer item = idlist.get(i);// item是集合成员
separator:集合成员之间的分隔符。 sql.append(","); //集合成员之间的分隔符
#{item 的值}:获取集合成员的值。
第一种方式:
//foreach-1
List<Student> selectForeachOne(List<Integer> idlist);
<!--foreach第一种方式, 循环简单类型的List-->
<select id="selectForeachOne" resultType="com.bjpowernode.domain.Student">
select * from student
<if test="list !=null and list.size>0">
where id in
<foreach collection="list" open="(" close=")" separator="," item="myid">
#{myid}
</foreach>
</if>
</select>
@Test
public void testSelectForeachOne(){
//1.获取SqlSession
SqlSession session = MyBatisUtil.getSqlSession();
//2.获取dao的代理
StudentDao dao = session.getMapper(StudentDao.class);
List<Integer> idlist = new ArrayList<>();
idlist.add(1001);
idlist.add(1002);
idlist.add(1003);
List<Student> students = dao.selectForeachOne(idlist);
students.forEach( stu-> System.out.println("stu=="+stu));
//3.关闭SqlSession对象
session.close();
}
第二种方式:
//foreach-2
List<Student> selectForeachTwo(List<Student> studentList);
<!--foreach第二种方式, 循环的List<Student>-->
<select id="selectForeachTwo" resultType="com.bjpowernode.domain.Student">
select * from student
<if test="list != null and list.size>0">
where id in
<foreach collection="list" open="(" close=")" separator="," item="stu">
#{stu.id}
</foreach>
</if>
</select>
@Test
public void testSelectForeachTwo(){
//1.获取SqlSession
SqlSession session = MyBatisUtil.getSqlSession();
//2.获取dao的代理
StudentDao dao = session.getMapper(StudentDao.class);
List<Student> list = new ArrayList<>();
Student s1 = new Student();
s1.setId(1001);
Student s2 = new Student();
s2.setId(1002);
list.add(s1);
list.add(s2);
List<Student> students = dao.selectForeachTwo(list);
students.forEach( stu-> System.out.println("stu=="+stu));
//3.关闭SqlSession对象
session.close();
}
sql标签
sql标签标示 一段sql代码, 可以是表名,几个字段, where条件都可以, 可以在其他地方复用sql标签的内容。
使用方式:
1) 在mapper文件中定义 sql代码片段 <sql id="唯一字符串"> 部分sql语句 </sql>
2)在其他的位置,使用include标签引用某个代码片段
例如:
<!--定义代码片段-->
<sql id="selectStudent">
select * from student
</sql>
<sql id="studentFieldList">
id,name,email
</sql>
<select id="selectIf" resultType="com.bjpowernode.domain.Student">
<include refid="selectStudent" />
where id=-1
<if test="name !=null and name!=''">
or name = #{name}
</if>
<if test="age >0">
or age < #{age}
</if>
</select>
<!--where-->
<select id="selectWhere" resultType="com.bjpowernode.domain.Student">
select <include refid="studentFieldList"/> from student
<where>
<if test="name !=null and name!=''">
or name = #{name}
</if>
<if test="age >0">
or age < #{age}
</if>
</where>
</select>