1.1 SqlSessionFactoryBuilder
通过SqlSessionFactoryBuilder创建会话工厂SqlSessionFactory
将SqlSessionFactoryBuilder当成一个工具类使用即可,不需要使用单例管理,在需要创建SqlSessionFactory的时候,只需要new一次SqlSessionBuilder。
1.2 SqlSessionFactory
通过SqlSessionFactory创建SqlSession,使用单例模式管理SqlSessionFactory(工厂一旦创建使用一个实例)
mybatis和spring整合后,使用单例模式管理sqlSessionFactory
1.3 SqlSession
SqlSession是一个面向用户(程序员)的接口
SqlSession中提供了很多操作数据库的方法,如:selectOne、selectList
SqlSession是线程不安全的,在SqlSession实现类中除了有接口中的方法(操作数据库的方法)还有数据域属性
SqlSession最佳应用场合在方法体类,定义成局部变量使用
2.2 原始Dao层开发方法
需要写dao接口和dao实现类,需要向dao实现类中注入SqlSessionFactory创建Sqlsession
接口实现类:
private SqlSessionFactory sqlSessionFactory;
public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
}
@Override
public User findUserById(int id) throws Exception {
// TODO Auto-generated method stub
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = sqlSession.selectOne("test.findUserById", id);
sqlSession.close();
return user;
}
@Override
public List<User> findUserByName(String username) throws Exception {
// TODO Auto-generated method stub
SqlSession sqlSession = sqlSessionFactory.openSession();
List<User> list = sqlSession.selectList("test.findUserByName",username);
sqlSession.close();
return list;
}
@Override
public void deleteUser(int id) throws Exception {
// TODO Auto-generated method stub
SqlSession sqlSession = sqlSessionFactory.openSession();
sqlSession.delete("test.deleteUser", id);
sqlSession.commit();
sqlSession.close();
}
开发问题: a.dao接口实现类中存在大量的模版方法
b.调用sqlSession方法时将statement的id硬编码了
c.调用sqlSession方法时传入的变量,由于sqlSession是泛型,即使变量类型传入错误,在编译阶段也不会报错,不利于开发
2.3 mapper代理方法
需要mapper接口,相当于dao接口,
还需要编写mapper映射文件,遵循开发规范,mybaties则可以自动生成mapper接口实现代理对象
规范:
a.namespace等于mapper接口的地址
b.mapper.java接口中的方法名和mapper.xml中statement的id一致
c.mapper.java接口中的输入参数类型和mapper.xml中指定类型一致
d.mapper.java接口中返回值和mapper.xml中指定返回类型一致
使用mapper代理:
public void testFindUserById() throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
//创建UserMapper对象,mybatis自动生成mapper代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//调用userMapper方法
User user = userMapper.findUserById(2);
System.out.println(user);
}
增加了映射文件需要在SqlMapConfig.xml中配置
3.1 SqlMapConfig.xml
properties(属性)
settings(全局配置参数)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境集合属性对象)
environment(环境子属性对象)
transactionManager(事务管理)
dataSource(数据源)
mappers(映射器)
3.2 properties属性
需求:将数据库连接参数单独配置在db.properties中,只需要在SqlMapConfig中加载db.properties的属性值,在SqlMapConfig中不用对数据库连接参数硬编码。
将数据库连接参数只配置在db.properties中的原因:方便对参数进行统一管理,其他xml文件可以引用db.properties
配置:
<configuration>
<properties resource="db.properties"></properties>
<!-- 和spring整合后 environment配置将废除 -->
<environments default="development">
<!-- 使用JDBC事务管理 -->
<environment id="development">
<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>
<mappers >
<!-- 加载映射文件 -->
<mapper resource="sqlMap/UserMapper.xml"></mapper>
</mappers>
</configuration>
注意:Mybatis将按照下面的顺序加载属性
1.在properties元素体内定义的属性首先被读取
2.然后会读取properties元素中resource或url加载的属性,会覆盖已读取的同名属性
3.最后读取parameterType传递的属性,会覆盖已读取的同名属性
因此,parameterType传递的属性具有最高优先级,#{name}传入的参数会覆盖之前的name属性
建议:不要在properties元素体内添加任何属性值,只将属性值定义在properties文件中,在properties文件中定义属性名要有一定的特殊性,如xxx.xxxx.xxx
3.3 setting全局参数配置
mybaties在运行时可以调整一些运行参数,如:开启二级缓存、开启延迟加载
全局参数会影响mybatis的运行行为
3.4 typeAliases别名
需求:在mapper.xml中定义了很多的statement,statement需要parameterType和resultType指定输入输出类型。如果在指定类型时输入类型的全路径,不便进行开发,可以针对输入输出参数定义一些别名。
mybaties默认支持别名,针对pojo需要自定义别名
配置:
<typeAliases>
<!-- 单个别名定义 -->
<typeAlias type="mybatis.po.User" alias="user"/>
<!-- 批量定义别名
指定报名,自动扫描包中的pojo类,自动定义别名,别名为类名,首字母可大写可小写 -->
<package name="mybatis.po"/>
</typeAliases>
3.5 typeHandlers类型处理器
mybatis中通过typeHandlers完成jdbc类型和java类似的转换
通常情况下,mybatis提供的类型处理器满足日常需要,不需要自定义
3.5 mappers
<mappers >
<!-- 加载映射文件 -->
<!-- 通过resource加载单个映射文件 -->
<mapper resource="sqlMap/UserMapper.xml"></mapper>
<!-- 通过mapper接口加载映射文件,需要将mapper接口类名和mapper.xml映射文件名称保持一致,并且在一个目录中
以上条件前提,使用mapper代理的方法 -->
<mapper class="mybatis.dao.UserMapper"></mapper>
<!-- 批量加载mapper 指定mapper接口的包名,自动扫描包中所有mapper进行加载
需要将mapper接口类名和mapper.xml映射文件名称保持一致,并且在一个目录中
以上条件前提,使用mapper代理的方法
-->
<package name="mybatis.dao"/>
</mappers>
推荐使用批量加载
3.6 输入映射(传递pojo的包装对象)
需求:完成用户信息的综合查询,需要传入很多查询条件(可能包括用户信息、商品信息、订单信息)
使用自定义包装类型的pojo,包装自己需要的复杂查询条件
在UserMapper.xml中定义用户信息综合查询(查询条件复杂,通过高级查询进行复杂关联查询)
3.7 输出映射
3.7.1 resultType
使用resultType进行映射,只有查询出来的列名和pojo的属性名一致,该列才可以映射成功
如果查询出来的列名和pojo的属性名完全不一致,则没有创建pojo对象
只要查询出来的列名和pojo的属性名有一个一致,就会创建pojo对象
3.7.2 resultMap
如果查询出来的列名和pojo的属性名不一致,通过定义一个resultMap对列名和属性名之间作一个映射关系
配置:
<!-- type result最终映射成的java对象类型,可以使用别名
id 对resultMap的唯一表示 -->
<resultMap type="user" id="userResultMap">
<!-- id查询结果集中的唯一标识
column :查询出来的别名
property:type指定的pojo类型中的属性名
最终resultMap对column和property作一个映射关系 -->
<id column="id_" property="id"/>
<!-- result:对普通列名映射定义 -->
<result column="username_" property="username"/>
</resultMap>
4 动态sql
4.1 什么是动态sql
mybatis核心,对sql语句进行灵活操作,通过表达式进行判断,对sql进行灵活拼接、组装
4.2 if判断
<select id="findUserList" parameterType="mybatis.po.User" resultType="mybatis.po.User">
select * from users
<where>
<!-- where自动去掉条件中的第一个and -->
<if test="id!=null and id!=''">
and id=#{id}
</if>
<if test="username!=null and username!=''">
and username like #{username}
</if>
</where>
</select>
4.3 sql片段
将实现动态sql片段的代码抽取出来,组成一个sql片段,其他statement中可以引用该sql片段
配置:
<!-- id:sql片段的唯一标识
是基于单表来定义的sql片段,这样可重用性高
在sql片段中不要包含where -->
<sql id="query_user_where">
<if test="id!=null and id!=''">
and id=#{id}
</if>
<if test="username!=null and username!=''">
and username like #{username}
</if>
</sql>
引用:<select id="findUserList" parameterType="mybatis.po.User" resultType="mybatis.po.User">
select * from users
<where>
<!-- where自动去掉条件中的第一个and -->
<include refid="query_user_where"></include>
<!-- 引用的id不在本mapper中,需要在前面加namespace -->
</where>
</select>
4.4 foreach
向sql传递数组或list,使用foreach解析
配置: <!-- 使用foreach遍历传入ids
collection:指定输入对象中集合属性
item:每个遍历生成对象
open:开始遍历时拼接的串
close:结束遍历时拼接的串
separator:遍历的两个对象之间需要拼接的串 -->
<if test="ids!=null">
<!-- 实现以下sql拼接: AND (id=1 OR id=2 OR id=3) -->
<foreach collection="ids" item="id" open="AND (" close=")" separator="OR" ></foreach>
</if>
通过SqlSessionFactoryBuilder创建会话工厂SqlSessionFactory
将SqlSessionFactoryBuilder当成一个工具类使用即可,不需要使用单例管理,在需要创建SqlSessionFactory的时候,只需要new一次SqlSessionBuilder。
1.2 SqlSessionFactory
通过SqlSessionFactory创建SqlSession,使用单例模式管理SqlSessionFactory(工厂一旦创建使用一个实例)
mybatis和spring整合后,使用单例模式管理sqlSessionFactory
1.3 SqlSession
SqlSession是一个面向用户(程序员)的接口
SqlSession中提供了很多操作数据库的方法,如:selectOne、selectList
SqlSession是线程不安全的,在SqlSession实现类中除了有接口中的方法(操作数据库的方法)还有数据域属性
SqlSession最佳应用场合在方法体类,定义成局部变量使用
2.2 原始Dao层开发方法
需要写dao接口和dao实现类,需要向dao实现类中注入SqlSessionFactory创建Sqlsession
接口实现类:
private SqlSessionFactory sqlSessionFactory;
public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
}
@Override
public User findUserById(int id) throws Exception {
// TODO Auto-generated method stub
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = sqlSession.selectOne("test.findUserById", id);
sqlSession.close();
return user;
}
@Override
public List<User> findUserByName(String username) throws Exception {
// TODO Auto-generated method stub
SqlSession sqlSession = sqlSessionFactory.openSession();
List<User> list = sqlSession.selectList("test.findUserByName",username);
sqlSession.close();
return list;
}
@Override
public void deleteUser(int id) throws Exception {
// TODO Auto-generated method stub
SqlSession sqlSession = sqlSessionFactory.openSession();
sqlSession.delete("test.deleteUser", id);
sqlSession.commit();
sqlSession.close();
}
开发问题: a.dao接口实现类中存在大量的模版方法
b.调用sqlSession方法时将statement的id硬编码了
c.调用sqlSession方法时传入的变量,由于sqlSession是泛型,即使变量类型传入错误,在编译阶段也不会报错,不利于开发
2.3 mapper代理方法
需要mapper接口,相当于dao接口,
还需要编写mapper映射文件,遵循开发规范,mybaties则可以自动生成mapper接口实现代理对象
规范:
a.namespace等于mapper接口的地址
b.mapper.java接口中的方法名和mapper.xml中statement的id一致
c.mapper.java接口中的输入参数类型和mapper.xml中指定类型一致
d.mapper.java接口中返回值和mapper.xml中指定返回类型一致
使用mapper代理:
public void testFindUserById() throws Exception {
SqlSession sqlSession = sqlSessionFactory.openSession();
//创建UserMapper对象,mybatis自动生成mapper代理对象
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//调用userMapper方法
User user = userMapper.findUserById(2);
System.out.println(user);
}
增加了映射文件需要在SqlMapConfig.xml中配置
3.1 SqlMapConfig.xml
properties(属性)
settings(全局配置参数)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境集合属性对象)
environment(环境子属性对象)
transactionManager(事务管理)
dataSource(数据源)
mappers(映射器)
3.2 properties属性
需求:将数据库连接参数单独配置在db.properties中,只需要在SqlMapConfig中加载db.properties的属性值,在SqlMapConfig中不用对数据库连接参数硬编码。
将数据库连接参数只配置在db.properties中的原因:方便对参数进行统一管理,其他xml文件可以引用db.properties
配置:
<configuration>
<properties resource="db.properties"></properties>
<!-- 和spring整合后 environment配置将废除 -->
<environments default="development">
<!-- 使用JDBC事务管理 -->
<environment id="development">
<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>
<mappers >
<!-- 加载映射文件 -->
<mapper resource="sqlMap/UserMapper.xml"></mapper>
</mappers>
</configuration>
注意:Mybatis将按照下面的顺序加载属性
1.在properties元素体内定义的属性首先被读取
2.然后会读取properties元素中resource或url加载的属性,会覆盖已读取的同名属性
3.最后读取parameterType传递的属性,会覆盖已读取的同名属性
因此,parameterType传递的属性具有最高优先级,#{name}传入的参数会覆盖之前的name属性
建议:不要在properties元素体内添加任何属性值,只将属性值定义在properties文件中,在properties文件中定义属性名要有一定的特殊性,如xxx.xxxx.xxx
3.3 setting全局参数配置
mybaties在运行时可以调整一些运行参数,如:开启二级缓存、开启延迟加载
全局参数会影响mybatis的运行行为
3.4 typeAliases别名
需求:在mapper.xml中定义了很多的statement,statement需要parameterType和resultType指定输入输出类型。如果在指定类型时输入类型的全路径,不便进行开发,可以针对输入输出参数定义一些别名。
mybaties默认支持别名,针对pojo需要自定义别名
配置:
<typeAliases>
<!-- 单个别名定义 -->
<typeAlias type="mybatis.po.User" alias="user"/>
<!-- 批量定义别名
指定报名,自动扫描包中的pojo类,自动定义别名,别名为类名,首字母可大写可小写 -->
<package name="mybatis.po"/>
</typeAliases>
3.5 typeHandlers类型处理器
mybatis中通过typeHandlers完成jdbc类型和java类似的转换
通常情况下,mybatis提供的类型处理器满足日常需要,不需要自定义
3.5 mappers
<mappers >
<!-- 加载映射文件 -->
<!-- 通过resource加载单个映射文件 -->
<mapper resource="sqlMap/UserMapper.xml"></mapper>
<!-- 通过mapper接口加载映射文件,需要将mapper接口类名和mapper.xml映射文件名称保持一致,并且在一个目录中
以上条件前提,使用mapper代理的方法 -->
<mapper class="mybatis.dao.UserMapper"></mapper>
<!-- 批量加载mapper 指定mapper接口的包名,自动扫描包中所有mapper进行加载
需要将mapper接口类名和mapper.xml映射文件名称保持一致,并且在一个目录中
以上条件前提,使用mapper代理的方法
-->
<package name="mybatis.dao"/>
</mappers>
推荐使用批量加载
3.6 输入映射(传递pojo的包装对象)
需求:完成用户信息的综合查询,需要传入很多查询条件(可能包括用户信息、商品信息、订单信息)
使用自定义包装类型的pojo,包装自己需要的复杂查询条件
在UserMapper.xml中定义用户信息综合查询(查询条件复杂,通过高级查询进行复杂关联查询)
3.7 输出映射
3.7.1 resultType
使用resultType进行映射,只有查询出来的列名和pojo的属性名一致,该列才可以映射成功
如果查询出来的列名和pojo的属性名完全不一致,则没有创建pojo对象
只要查询出来的列名和pojo的属性名有一个一致,就会创建pojo对象
3.7.2 resultMap
如果查询出来的列名和pojo的属性名不一致,通过定义一个resultMap对列名和属性名之间作一个映射关系
配置:
<!-- type result最终映射成的java对象类型,可以使用别名
id 对resultMap的唯一表示 -->
<resultMap type="user" id="userResultMap">
<!-- id查询结果集中的唯一标识
column :查询出来的别名
property:type指定的pojo类型中的属性名
最终resultMap对column和property作一个映射关系 -->
<id column="id_" property="id"/>
<!-- result:对普通列名映射定义 -->
<result column="username_" property="username"/>
</resultMap>
4 动态sql
4.1 什么是动态sql
mybatis核心,对sql语句进行灵活操作,通过表达式进行判断,对sql进行灵活拼接、组装
4.2 if判断
<select id="findUserList" parameterType="mybatis.po.User" resultType="mybatis.po.User">
select * from users
<where>
<!-- where自动去掉条件中的第一个and -->
<if test="id!=null and id!=''">
and id=#{id}
</if>
<if test="username!=null and username!=''">
and username like #{username}
</if>
</where>
</select>
4.3 sql片段
将实现动态sql片段的代码抽取出来,组成一个sql片段,其他statement中可以引用该sql片段
配置:
<!-- id:sql片段的唯一标识
是基于单表来定义的sql片段,这样可重用性高
在sql片段中不要包含where -->
<sql id="query_user_where">
<if test="id!=null and id!=''">
and id=#{id}
</if>
<if test="username!=null and username!=''">
and username like #{username}
</if>
</sql>
引用:<select id="findUserList" parameterType="mybatis.po.User" resultType="mybatis.po.User">
select * from users
<where>
<!-- where自动去掉条件中的第一个and -->
<include refid="query_user_where"></include>
<!-- 引用的id不在本mapper中,需要在前面加namespace -->
</where>
</select>
4.4 foreach
向sql传递数组或list,使用foreach解析
配置: <!-- 使用foreach遍历传入ids
collection:指定输入对象中集合属性
item:每个遍历生成对象
open:开始遍历时拼接的串
close:结束遍历时拼接的串
separator:遍历的两个对象之间需要拼接的串 -->
<if test="ids!=null">
<!-- 实现以下sql拼接: AND (id=1 OR id=2 OR id=3) -->
<foreach collection="ids" item="id" open="AND (" close=")" separator="OR" ></foreach>
</if>