1.到目前为止引入的依赖
spring mybatis mysql mybatis-spring druid
2.编写spring.xml
整合:spring接管mybatis中SqlSessionFactory对象的创建
<!--创建数据源-->
<bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/sm_learn?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull"></property>
<property name="username" value="root"></property>
<property name="password" value="123456"></property>
</bean>
<!--创建sqlSessionFactory-->
<bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactoryBean">
<!--依赖的数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
3.建表
CREATE TABLE IF NOT EXISTS CF_USER_T(
USER_ID INT PRIMARY KEY COMMENT'用户登录ID',
USER_NAME VARCHAR(200) COMMENT'用户名',
USER_AGE INT COMMENT'年龄',
USER_SEX INT COMMENT'用户性别',
USER_TEL VARCHAR(20) COMMENT'用户电话号码',
USER_PASSWORD VARCHAR(20) COMMENT'用户登录密码'
) COMMENT'用户信息表'
4.实体类
package com.cf.testsmdao.entity;
import lombok.Data;
/**
* User: 晨风
* <p>
* Author:
* Date: 22:56
*/
@Data
public class User {
private Long userId;
private int userAge;
private int userSex;
private String userName;
private String password;
private String userTel;
public User() {
}
public User(Long userId, int userAge, int userSex, String userName, String password, String userTel) {
this.userId = userId;
this.userAge = userAge;
this.userSex = userSex;
this.userName = userName;
this.password = password;
this.userTel = userTel;
}
}
5.开发dao层接口
package com.cf.testsmdao.dao;
import com.cf.testsmdao.entity.User;
import java.util.List;
/**
* @author yourname
* @date 2021/4/16 23:04
*/
public interface UserDAO {
void save();
List<User> find();
}
6.开发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.cf.testsmdao.dao.UserDAO">
<select id="find" resultType="com.cf.testsmdao.entity.User">
select
USER_ID userId,
USER_NAME userName,
USER_AGE userAge,
USER_SEX userSex,
USER_TEL userTel,
USER_PASSWORD password from CF_USER_T
</select>
</mapper>
7.启动工厂获取SqlSessionFactory
package com.cf.testsmdao.test;
import com.cf.testsmdao.dao.UserDAO;
import com.cf.testsmdao.entity.User;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.jupiter.api.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;
/**
* User: 裴晨风
* <p>
* Author:
* Date: 23:34
*/
public class TestMapper {
@Test
public void test() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("com/cf/sm2/spring.xml");
SqlSessionFactory sessionFactory = (SqlSessionFactory) context.getBean("sqlSessionFactoryBean");
SqlSession sqlSession = sessionFactory.openSession();
UserDAO userDAO = sqlSession.getMapper(UserDAO.class);
userDAO.find().forEach(System.out::println);
//User(userId=1, userAge=18, userSex=1, userName=小一, password=a11111, userTel=11111)
//User(userId=2, userAge=19, userSex=2, userName=小二, password=a22222, userTel=22222)
}
}
8.改进
虽然学到这里,感觉框架的封装已经很妙了,但是我们可以从test中发现,每次要获取DAO,我们都需要走三个步骤。
1).获取SqlSessionFactory ->{SqlSessionFactory sessionFactory = (SqlSessionFactory) context.getBean("sqlSessionFactoryBean");}
2).通过SqlSessionFactory 创建SqlSession -> {SqlSession sqlSession = sessionFactory.openSession();}
3).通过SqlSession 获取到dao的mapper文件-> {UserDAO userDAO = sqlSession.getMapper(UserDAO.class);}
然后,我们通过调用dao层的方法后,会执行mapper中的对应方法的sql语句,对数据库进行crud。
但是如果每次调用dao层时,都要经过这三个步骤的话,未免太过繁琐。因为我们不关心取mapper的过程(创建mybatis的核心对象SqlSessionFactory->创建SqlSession ->sqlSession.getMapper(UserDAO.class)),我们只要最后获取到mapper就可以了,当然是一步到位最好。
如何一步到位呢?
首先,我们可以回想下spring的AOP思想,将这三步封装成一个前置通知是否可以呢?将切面设定为所有dao层内的方法,然后每次调用dao方法的时候,执行前置通知获取mapper。
思考了下,应该是不可以。对比下之前未整合mybatis时的AOP编程代码,可以看到,当时的dao层方法可以执行,是基于有其对应的实现类。而整合mybatis后,我的理解是此时的dao层的实现其实就是对应的mapper文件中的sql语句。而获取mapper正是dao层得以实现的必要条件。如果封装成前置通知,那大概意思就是这样了:执行dao的方法前,在前置通知中获取对应的mapper文件。其中有两点有待商榷:1.dao的方法能够执行么?2.通知的定义是对业务功能之外的附加功能的实现。即:前置通知可以是业务功能实现的必要条件吗?以我现在的只是储备量来思考的话,是不能够的。
mybatis想的很周到:
mybatis-spring jar包中的org.mybatis.spring.mapper.MapperFactoryBean已经封装了getMapper的过程
<!-- 创建DAO组件类-->
<!-- 创建userDAO-->
<bean id="userDAO" class="org.mybatis.spring.mapper.MapperFactoryBean">
<!-- 注入SqlSessionFactory-->
<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
<!-- 注入创建DAO接口类型 注入接口的全限定名 包.接口名-->
<property name="mapperInterface" value="com.cf.testsmdao.dao.UserDAO"></property>
</bean>
<!-- 创建studentDAO-->
<bean id="studentDAO" class="org.mybatis.spring.mapper.MapperFactoryBean">
<!-- 注入SqlSessionFactory-->
<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
<!-- 注入创建DAO接口类型 注入接口的全限定名 包.接口名-->
<property name="mapperInterface" value="com.cf.testsmdao.dao.studentDAO"></property>
</bean>
于是一步到位,我们想要获取哪个mapper,只需要在配置文件中注入即可value="com.cf.testsmdao.dao.studentDAO"。
此时,再回顾一下整个整合的思路:
1.什么是复杂对象?复杂对象怎么创建?
a.复杂对象:不可通过new创建实例的对象
b.复杂对象怎么创建:创建对应bean类,在类中通过重写getObject自定义创建复杂对象
2.spring整合mybatis的切入点是什么?
掌握mybatis的核心对象的创建
3.SqlSessionFactory核心对象如何通过spring工厂创建?
a.引入mybatis-spring jar包
b.注入依赖-数据源dataSource
4.spring工厂创建SqlSessionFactory后,怎么通过SqlSessionFactory获取到dao的mapper文件?
a.SqlSessionFactory bean中 注入依赖--要获取的mapper文件
b.获取SqlSessionFactory
c.通过SqlSessionFactory 创建SqlSession
d.通过SqlSession 获取到dao的mapper文件
5.如何封装获取mapper的过程?或者说直接获取dao层?
a.引入org.mybatis.spring.mapper.MapperFactoryBean
b.注入依赖--含有mapper文件依赖的SqlSessionFactory bean
c.注入依赖--获取哪个dao的mapper文件
感想:
整合mybatis之前,通过spring生成一个对象是那么的简单,工厂中对象的路径一写,id一确定,要啥属性就注入啥,工厂启动后拿来对象就能用。
整合mybatis后,先是复杂对象的创建,然后再是复杂对象创建的封装,封装的过程中又伴随着许多复杂的依赖注入。所幸,结果是一样的,还是工厂启动后拿来就能用。
另:在工作中springboot用起来是真的方便,没有这么多依赖、配置。实现持久化也不需要先得到SqlSessionFactory 再得到xxx最后得到mapper。只要mapper中namespace关联上dao层路径,方法id一致,直接new出来就能实现。
会不会是,越高端的框架使用起来就越简单呢?然而当你用起来很简单的时候,你就忽略了他的底层实现,想都不会想一下。
希望我一直努力学下去,对框架封装的那些思想原理不再是一头雾水。
最好的软件是傻子都会用的。
但是身为一个开发者,不能真的像个傻子一样对自己使用的技术一无所知!