在讲解原始Dao开发,和Mapper代理开发之前,我们先从原始JDBC和Spring整合JDBC讲起,纵观整个技术设计思路,我们就更加容易理解框架原理。
原始JDBC:
try {
// 原始Jdbc操作
String sql = "insert into celebrity (c_id,c_name) values (?,?)";
Connection conn = null;
PreparedStatement pStatement = null;
// 加载数据库驱动
Class.forName("com.mysql.jdbc.Driver");
// 获取数据库连接对象
conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/entertainment?charset=utf8&serverTimezone=UTC", "root", "root");
// 业务
pStatement = conn.prepareStatement(sql);
pStatement.setInt(1, celebrity.getID());
pStatement.setString(2, celebrity.getName());
//返回的结果是 the row counts(行数) that were updated
result = pStatement.executeUpdate();
// 获取返回结果
if (0 == result) {
// 事务回滚
return result;
}
// 关闭资源
pStatement.close();
conn.close();
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
原始JDBC时候我么发现,每次对于数据库的操作都需要开发人员加载相关驱动创建相关连接对象,造成数据库和系统的资源的大量浪费,效率不高。
于是Spring整合JDBC解决了原始JDBC的部分问题。
1.通过JDBCTemplate对象,完成了数据源的加载(内部完成connection连接对象的管理)
2.通过Spring IOC容器完成相关对象的创建,解决了对象之间的依赖问题,
3.简化了JDBC的操作,将重复部分从代码中抽取出来,同时对于连接对象的管理也提高了数据库资源的利用率。
详情参考https://blog.csdn.net/Stephen_mu/article/details/87924158
不足:1. 赤裸裸的sql语句,sql语句的参数和系统中的pojo不存在任何映射关系,返回结果也与pojo不存在任何映射关系(这些都需要开发人员手动处理,无形中增加了代码量)
2. Sql硬编码,稍微更改需求就需要重新编写方法,导致系统代码冗余。
3. 数据库的相关操作,没有任何优化。不存在任何缓存机制,对于重复的查询操作,需要每次访问数据库,降低了数据库的性能。
Mybatis半自动框架的引入:
详情参考:https://blog.csdn.net/Stephen_mu/article/details/88150805
然而对于Mybatis的实现存在两种方式:
SpringMybatis整合 原始Dao开发:
前提加载mybatisConfig.xml配置文件(里面配置了mapper(映射文件)配置)
1.在Dao层方法内部通过sqlSession对象执行相关操作,
2.相关操作内部会读取Mapper.xml(映射文件),
3.通过Executor(baseExecutor,CachingExecutor)执行相关的数据库操作
4.创建MapperedStatement对象,根据一些唯一的key创建Cache对象
5.按照映射规则返回相关结果
1.配置mapper.xml配置文件
<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--ORM namespace 起到sql分类华管理,即sql隔离,namespace即为mapper.java文件的全路径(相当于指明dao类) -->
<mapper namespace="celebrityStatement">
<!--整合ehcache分布式缓存,还需要配置ehcache配置文件 -->
<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
<!-- id:对于resultMap的唯一标识,type:指定返回结果类型,可以使用别名 -->
<resultMap type="celebrity" id="celebrityResultMap">
<!--id:标识查询结果中唯一标识 -->
<!--column:查询出来的列名 -->
<!--property:type指定pojo类型中的属性名 -->
<id column="id" property="c_id"></id>
<!--result:对普通名定义映射 -->
<!--column:查询出来的列名 -->
<!--property:type指定pojo类型中的属性名 -->
<result column="name" property="c_name"></result>
</resultMap>
<!--配置sql语句 -->
<!--标签的id属性起到标识标签的作用,同时由于mybatis框架会将sql语句封装到一个MappedStatement底层对象中,此id也就相当于MappedStatement对象的id -->
<!--1.将SQL语句侧参数通过占位符(#{})提交到动态提交到sql语句中 -->
<!--#{}表示一个占位符 -->
<!--2.将sql语句的返回结果和java实体对象映射 -->
<!--3.parameterType:表示sql语句输入参数的类型,注意如果参数为简单类型,则占位符参数名可以任意定义 -->
<!--4.resultType:表示sql语句执行结果所映射的java对象的类型 (单条记录) -->
<select id="getById" parameterType="int"
resultMap="celebrityResultMap">
select c_id as id, c_name as name from celebrity where
c_id in
<foreach collection="list" item="c_id" index="index" open="("
separator="," close=")">
#{c_id}
</foreach>
</select>
</mapper>
2. spring整合mybatis配置
<!--Spring整合Mybatis,在这里配置SqlSessionFactoryBean -->
<bean id="sqlSessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean">
<!--加载Mybatis配置文件,将sqlSessionFactory交给springIOC容器管理 -->
<property name="configuration"
value="classpath:resources/mybatis/MybatisConfig.xml"></property>
<property name="dataSource" ref="dataSource" />
</bean>
<!--这里配置dao注入,经过试验测试里面的sqlSessionFactory在这里似乎不能采用注解注入-->
<bean id="celebrityDao" class="com.ncs.dao.CelebrityDaoImpl">
<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
</bean>
3.Mybatis配置文件
<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 1、mybatis使用properties来引入外部properties配置文件的内容 resource 引入类路径下资源 url 引入网络路径或磁盘路径下资源 -->
<!-- <properties resource="resources/jdbc/jdbc.properties"></properties> -->
<settings>
<!--延迟加载的全局开关 -->
<setting name="lazyLoadingEnabled" value="true" />
<!--积极加载的全局开关 -->
<setting name="aggressiveLazyLoading" value="false" />
<!--开启二级缓存 -->
<setting name="cacheEnabled" value="true" />
</settings>
<typeAliases>
<!-- <typeAlias type="com.ncs.entity.Celebrity" alias="celebrity"/> -->
<!--别名批量扫描,pojo包路径 -->
<package name="com.ncs.pojo" />
</typeAliases>
<!--加载ORM映射文件 -->
<mappers>
<mapper resource="resources/mapper/CelebrityMapper.xml"></mapper>
<!--这种扫描似乎无法批量扫描resource路径下的资源,因为这是一个package标签-->
<!-- <package name="com.ncs.mapper" /> -->
</mappers>
</configuration>
最主要的要想使用到SqlSessionFactory,并且创建SqlSession对象,我们需要让Dao的实现类(daoImpl)继承SqlSessionDaoSupport
public class CelebrityDaoImpl extends SqlSessionDaoSupport implements CelebrityDao {
@Override
public Celebrity getById(int id) {
// 获取会话对象
SqlSession session = this.getSqlSession();
Celebrity result = session.selectOne("celebrityStatement.getById", id);
return result;
}
}
SpringMybatis整合 Mapper代理开发:
1.按照Mapper.xml映射配置文件的相关规则,书写Mapper.java接口(注意:Mapper接口,必须按照Mapper.xml映射相关规则,否则程序不成功)
注意修改mapper.xml中namespace值为mapper接口地址
2.通过SqlSession对象,依靠反射生成Mapper接口的代理对象
3.通过代理对象执行相关操作
4.通过Executor(baseExecutor,CachingExecutor)执行相关的数据库操作
5.创建MapperedStatement对象,根据一些唯一的key创建Cache对象
6.按照映射规则返回相关结果
1.mapper.xml配置文件 (注意mapper 的namespace(命名空间)需要设置为对应的mapper接口类)
<?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="com.ncs.mapper.UserMapper">
<resultMap id="BaseResultMap" type="com.ncs.pojo.User">
<!--
WARNING - @mbg.generated
This element is automatically generated by MyBatis Generator, do not modify.
-->
<result column="id" jdbcType="INTEGER" property="id" />
<result column="u_name" jdbcType="VARCHAR" property="uName" />
<result column="u_sex" jdbcType="CHAR" property="uSex" />
<result column="u_age" jdbcType="INTEGER" property="uAge" />
<result column="birthday" jdbcType="TIMESTAMP" property="birthday" />
<result column="address" jdbcType="VARCHAR" property="address" />
<result column="remark" jdbcType="VARCHAR" property="remark" />
</resultMap>
<sql id="Example_Where_Clause">
<!--
WARNING - @mbg.generated
This element is automatically generated by MyBatis Generator, do not modify.
-->
<where>
<foreach collection="oredCriteria" item="criteria" separator="or">
<if test="criteria.valid">
<trim prefix="(" prefixOverrides="and" suffix=")">
<foreach collection="criteria.criteria" item="criterion">
<choose>
<when test="criterion.noValue">
and ${criterion.condition}
</when>
<when test="criterion.singleValue">
and ${criterion.condition} #{criterion.value}
</when>
<when test="criterion.betweenValue">
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
</when>
<when test="criterion.listValue">
and ${criterion.condition}
<foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
#{listItem}
</foreach>
</when>
</choose>
</foreach>
</trim>
</if>
</foreach>
</where>
</sql>
<sql id="Update_By_Example_Where_Clause">
<!--
WARNING - @mbg.generated
This element is automatically generated by MyBatis Generator, do not modify.
-->
<where>
<foreach collection="example.oredCriteria" item="criteria" separator="or">
<if test="criteria.valid">
<trim prefix="(" prefixOverrides="and" suffix=")">
<foreach collection="criteria.criteria" item="criterion">
<choose>
<when test="criterion.noValue">
and ${criterion.condition}
</when>
<when test="criterion.singleValue">
and ${criterion.condition} #{criterion.value}
</when>
<when test="criterion.betweenValue">
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
</when>
<when test="criterion.listValue">
and ${criterion.condition}
<foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
#{listItem}
</foreach>
</when>
</choose>
</foreach>
</trim>
</if>
</foreach>
</where>
</sql>
<sql id="Base_Column_List">
<!--
WARNING - @mbg.generated
This element is automatically generated by MyBatis Generator, do not modify.
-->
id, u_name, u_sex, u_age, birthday, address, remark
</sql>
<select id="selectByExample" parameterType="com.ncs.pojo.UserExample" resultMap="BaseResultMap">
<!--
WARNING - @mbg.generated
This element is automatically generated by MyBatis Generator, do not modify.
-->
select
<if test="distinct">
distinct
</if>
<include refid="Base_Column_List" />
from user
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
<if test="orderByClause != null">
order by ${orderByClause}
</if>
</select>
<delete id="deleteByExample" parameterType="com.ncs.pojo.UserExample">
<!--
WARNING - @mbg.generated
This element is automatically generated by MyBatis Generator, do not modify.
-->
delete from user
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
</delete>
<insert id="insert" parameterType="com.ncs.pojo.User">
<!--
WARNING - @mbg.generated
This element is automatically generated by MyBatis Generator, do not modify.
-->
insert into user (id, u_name, u_sex,
u_age, birthday, address,
remark)
values (#{id,jdbcType=INTEGER}, #{uName,jdbcType=VARCHAR}, #{uSex,jdbcType=CHAR},
#{uAge,jdbcType=INTEGER}, #{birthday,jdbcType=TIMESTAMP}, #{address,jdbcType=VARCHAR},
#{remark,jdbcType=VARCHAR})
</insert>
<insert id="insertSelective" parameterType="com.ncs.pojo.User">
<!--
WARNING - @mbg.generated
This element is automatically generated by MyBatis Generator, do not modify.
-->
insert into user
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">
id,
</if>
<if test="uName != null">
u_name,
</if>
<if test="uSex != null">
u_sex,
</if>
<if test="uAge != null">
u_age,
</if>
<if test="birthday != null">
birthday,
</if>
<if test="address != null">
address,
</if>
<if test="remark != null">
remark,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
#{id,jdbcType=INTEGER},
</if>
<if test="uName != null">
#{uName,jdbcType=VARCHAR},
</if>
<if test="uSex != null">
#{uSex,jdbcType=CHAR},
</if>
<if test="uAge != null">
#{uAge,jdbcType=INTEGER},
</if>
<if test="birthday != null">
#{birthday,jdbcType=TIMESTAMP},
</if>
<if test="address != null">
#{address,jdbcType=VARCHAR},
</if>
<if test="remark != null">
#{remark,jdbcType=VARCHAR},
</if>
</trim>
</insert>
<select id="countByExample" parameterType="com.ncs.pojo.UserExample" resultType="java.lang.Long">
<!--
WARNING - @mbg.generated
This element is automatically generated by MyBatis Generator, do not modify.
-->
select count(*) from user
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
</select>
<update id="updateByExampleSelective" parameterType="map">
<!--
WARNING - @mbg.generated
This element is automatically generated by MyBatis Generator, do not modify.
-->
update user
<set>
<if test="record.id != null">
id = #{record.id,jdbcType=INTEGER},
</if>
<if test="record.uName != null">
u_name = #{record.uName,jdbcType=VARCHAR},
</if>
<if test="record.uSex != null">
u_sex = #{record.uSex,jdbcType=CHAR},
</if>
<if test="record.uAge != null">
u_age = #{record.uAge,jdbcType=INTEGER},
</if>
<if test="record.birthday != null">
birthday = #{record.birthday,jdbcType=TIMESTAMP},
</if>
<if test="record.address != null">
address = #{record.address,jdbcType=VARCHAR},
</if>
<if test="record.remark != null">
remark = #{record.remark,jdbcType=VARCHAR},
</if>
</set>
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
</update>
<update id="updateByExample" parameterType="map">
<!--
WARNING - @mbg.generated
This element is automatically generated by MyBatis Generator, do not modify.
-->
update user
set id = #{record.id,jdbcType=INTEGER},
u_name = #{record.uName,jdbcType=VARCHAR},
u_sex = #{record.uSex,jdbcType=CHAR},
u_age = #{record.uAge,jdbcType=INTEGER},
birthday = #{record.birthday,jdbcType=TIMESTAMP},
address = #{record.address,jdbcType=VARCHAR},
remark = #{record.remark,jdbcType=VARCHAR}
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
</update>
</mapper>
2.Mapper.java接口文件
package com.ncs.mapper;
import com.ncs.pojo.User;
import com.ncs.pojo.UserExample;
import java.util.List;
import org.apache.ibatis.annotations.Param;
public interface UserMapper {
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table user
*
* @mbg.generated
*/
long countByExample(UserExample example);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table user
*
* @mbg.generated
*/
int deleteByExample(UserExample example);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table user
*
* @mbg.generated
*/
int insert(User record);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table user
*
* @mbg.generated
*/
int insertSelective(User record);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table user
*
* @mbg.generated
*/
List<User> selectByExample(UserExample example);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table user
*
* @mbg.generated
*/
int updateByExampleSelective(@Param("record") User record, @Param("example") UserExample example);
/**
* This method was generated by MyBatis Generator.
* This method corresponds to the database table user
*
* @mbg.generated
*/
int updateByExample(@Param("record") User record, @Param("example") UserExample example);
}
3.配置mybatis配置文件,配置所有的mapper.xml文件
<?xml version="1.0" encoding="GBK"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 1、mybatis使用properties来引入外部properties配置文件的内容 resource 引入类路径下资源 url 引入网络路径或磁盘路径下资源 -->
<!-- <properties resource="resources/jdbc/jdbc.properties"></properties> -->
<settings>
<!--延迟加载的全局开关 -->
<setting name="lazyLoadingEnabled" value="true" />
<!--积极加载的全局开关 -->
<setting name="aggressiveLazyLoading" value="false" />
<!--开启二级缓存 -->
<setting name="cacheEnabled" value="true" />
</settings>
<typeAliases>
<!--单个别名设置 -->
<!-- <typeAlias type="com.ncs.entity.Celebrity" alias="celebrity"/> -->
<!--别名批量扫描,pojo包路径 -->
<package name="com.ncs.pojo" />
</typeAliases>
<!--加载ORM映射文件 -->
<!--当我们使用org.mybatis.spring.mapper.MapperScannerConfigurer进行mapper接口扫描时,如果将mapper.xml与mapper接口文件放在同一路径下,就不要配置此配置 -->
<mappers>
<mapper resource="resources/mapper/UserMapper.xml"></mapper>
<mapper resource="resources/mapper/OrderformMapper.xml"></mapper>
<mapper resource="resources/mapper/OrderdetailsMapper.xml"></mapper>
<mapper resource="resources/mapper/CommodityMapper.xml"></mapper>
<!-- 映射文件批量扫描,mapper映射文件-->
<package name="com.ncs.mapper" />
</mappers>
</configuration>
4.spring和mybatis整合,配置sqlSessionFactory,为mapper.java接口生成代理对象
<!--Spring整合Mybatis,在这里配置SqlSessionFactoryBean -->
<bean id="sqlSessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean">
<!--加载Mybatis配置文件,将sqlSessionFactory交给springIOC容器管理 -->
<property name="configLocation"
value="classpath:resources/mybatis/mybatisConfig.xml"></property>
<property name="dataSource" ref="dataSource" />
</bean>
<!--配置mapper,生成mapper接口的实现类,并且让SpringIOC容器来管理其代理类 -->
<!--这种配置将mapper接口文件和mapper映射文件放在同一目录下,则不再需要在mybatis配置文件中配置映射文件-->
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<!--指定mapper配置文件,根据mapper接口生成代理对象 -->
<property name="mapperInterface"
value="com.ncs.mapper.UserMapper"></property>
<!--由于在创建代理对象时也需要使用到sqlSessionFactory,所以我们将sqlSessionFactory、配置到MapperFactoryBean中 -->
<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
</bean>
不足:需要对所有的mapper.java接口进行配置,生成其代理对象,配置较为繁琐,冗余。
解决:org.mybatis.spring.mapper.MapperScannerConfiguer,,并且扫描所有的mapper.java接口
<!--Spring整合Mybatis,在这里配置SqlSessionFactoryBean -->
<bean id="sqlSessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean">
<!--加载Mybatis配置文件,将sqlSessionFactory交给springIOC容器管理 -->
<property name="configLocation"
value="classpath:resources/mybatis/mybatisConfig.xml"></property>
<property name="dataSource" ref="dataSource" />
</bean>
<!--org.mybatis.spring.mapper.MapperFactoryBean复杂配置的解决方案 开启mapper接口快速扫描,简化配置 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--指定mapper接口路径,一次性扫描所有的mapper接口 -->
<property name="basePackage"
value="com.ncs.mapper"></property>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
注意:
1.(ps:注入属性名称用sqlSessionFactoryBeanName而不是sqlSessionFactory是因为这个两个属性赋值的时机是不一样的,
上面bean那个应该是被动调用执行,当前这个bean是自动扫描
2. 注意:sqlSessionFactoryBeanName要用value而不用ref. 在mybatis-spring1.1.0以前,是通过
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>将SqlSessionFactory对象注入到 sqlSessionFactory,
这样做可能会有一个问题,就是在初始化MyBatis时,jdbc.properties文件还没被加载进来,
dataSource的属性值没有被替换,就开始构造sqlSessionFactory类,属性值就会加载失败。
在1.1.0以后,MapperScannerConfigure提供了String类型的sqlSessionFactoryBeanName,这样将bean name 注入到sqlSessionFactoryBeanName,这样就会等到spring初始化完成后,再构建sqlSessionFactory。