框架整合spring的目的:把该框架常用的工具对象交给spring管理,要用时从IOC容器中取mybatis对象。
在spring中应该管理的对象是sqlsessionfactory对象,工厂只允许被创建一次,所以需要创建一个工具类,把创建工厂的代码放在里面,后续直接调用工厂即可。
环境配置
先准备个数据库
CREATE TABLE `student` (
`id` int NOT NULL,
`name` varchar(30) COLLATE utf8mb3_bin DEFAULT NULL,
`age` int DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_bin
依赖引入:
<!--spring需要的坐标-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.18.RELEASE</version>
</dependency>
<!--2.mybatis需要的坐标-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.31</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId> <!--为mybatis配置的数据源-->
<version>1.2.8</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
<!--3.spring整合mybatis需要的-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
项目结构
创建相应的dao层,service层,pojo层
pojo
对应数据库的student表的属性写相应的setter和getter还有tostring以及一个无参构造
public class seudent {
private int id;
private String name;
private int age;
public int getId() {
return id;
}
public seudent() {
}
@Override
public String toString() {
return "seudent{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
dao层
实现对应的增删改查的接口语句,还要写mapper映射与接口语句对应,使mybatis能通过点的方式获取方法
public interface StudentDao {
public List<Student> selectAll();
public void insert(Student student);
public int delete(int id);
}
映射文件StudentDao.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--
namespqce:名称空间
id:sql语句的唯一标识
resultType:返回结果的类型
-->
<mapper namespace="org.example.dao.StudentDao">
<insert id="insert" parameterType="org.example.pojo.Student">
insert into student values(#{id},#{name},#{age});
</insert>
<delete id="delete" parameterType="int">
delete from student where id=#{id};
</delete>
<select id="selectAll" resultType="org.example.pojo.Student" parameterType="org.example.pojo.Student">
select * from student;
</select>
</mapper>
service层
需要操作持久层或实现查询,需要与dao层的方法对应
先写接口方法
public interface StudentService {
public List<Student> findAll();
public void add(Student student);
public int remove(int id);
}
再写实现类,需要调用Dao层的方法,所以需要先获取一个StudentDao对象通过对象调用方法
package org.example.service.impl;
import org.example.dao.StudentDao;
import org.example.pojo.Student;
import org.example.service.StudentService;
import java.util.List;
public class StudentServiceImpl implements StudentService {
private StudentDao studentDao;
public StudentDao getStudentDao() {
return studentDao;
}
public void setStudentDao(StudentDao studentDao) {
this.studentDao = studentDao;
}
@Override
public List<Student> findAll() {
return studentDao.selectAll();
}
@Override
public void add(Student student) {
studentDao.insert(student);
}
@Override
public int remove(int id) {
return studentDao.delete(id);
}
}
配置applicationContext.xml
SqlSessionFactoryBean有一个必须属性dataSource,另外其还有一个通用属性configLocation(用来指定mybatis的xml配置文件路径)。
其中dataSource使用阿里巴巴的druid作为数据源,druid需要db.propertis文件的内容如下
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test1
jdbc.username=root
jdbc.password=234799
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="mapperLocations" value="mapper/*xml"/> <!--默认查找resources下xml-->
<property name="dataSource" ref="dataSource"/>
<property name="typeAliasesPackage" value="org.example.pojo"/>
</bean>
<!--使用阿里巴巴提供的druid作为数据源,通过引入外部properties文件进行加载,并使用${}参数占位符的方式去读取properties中的值-->
<context:property-placeholder location="classpath:db.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
还需要生成dao层接口实现类的bean对象
<!--扫描接口包路径,生成包下所有接口的代理对象,并且放入spring容器中,后面可以直接用,自动生成mapper层(dao层)的代理对象-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="org.example.dao"/>
</bean>
还有service的接口实现类也要交给spring管理
service里面的依赖注入就交给spring来完成
<!--spring管理其他bean studentDao是spring根据mybatis自动生成的对象-->
<!--来自于MapperScannerConfigurer-->
<bean class="org.example.service.impl.StudentServiceImpl" id="studentService">
<property name="studentDao" ref="studentDao"/>
</bean>
在测试类中执行业务层操作
添加两个学生后查询一次所有元素输出,然后删除一个学生后再一次查询一次所有元素输出,成功输出,如下图所示
public class Main {
public static void main(String[] args) {
ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
StudentService studentService= (StudentService)ctx.getBean("studentService");
Student student=new Student();
student.setId(1);
student.setName("yhy");
student.setAge(18);
Student student2=new Student();
student2.setId(2);
student2.setName("yxc");
student2.setAge(30);
studentService.add(student);
studentService.add(student2);
List<Student> students=studentService.findAll();
System.out.println(students);
studentService.remove(2);
students=studentService.findAll();
System.out.println(students);
}
}
以上就是用XML的方式spring整合mybatis,在applicationContext中进行了mybatis工具类的相关配置,不需要mybatis的配置文件
接下来就是用注解的方式整合mybatis
就是在上面的条件下把applicationContext.xml配置文件当中的内容转换成注解
第一步
将service层的bean对象改用注解和组件扫描的方式创建,表现为将原本的service的bean标签去除,并在studentserviceImpl中加个@service注解,并定义一个名字,方便主函数中调用
(组件扫描就是把所有加了相应注解的东西放到容器中去)
对于studentserviceImpl中的stduentDao对象使用@Autowired实现自动装配,同时,setter和getter方法也可以删除了
在主文件中使用组件名获取bean,不再是使用bean标签里面的id属性去获取,如下
第二步
新建一个配置类SpringConfig.calss而不是使用配置文件,加上
@Configuration 表明是一个配置类
@ComponentScan(value = {"org.example"}) 扫包的标签
然后将配置文件里面的bean全部移植到SpringConfig中去
首先是SqlSessionFactoryBean对象,写一个方法返回该对象,对于SqlSessionFactoryBean需要的属性都在里面一一赋予它,对于datasource先暂时置为空,下面要先完成datasource的bean管理,加上一个@bean放入容器管理
datasource的bean方法创建,datasource依赖于外部的数据源druid,所以又要先创建一个DruidDataSource的bean方法,DruidDataSource又依赖于一个db.properties,这里可以在配置类上使用一个注解@PropertySource("db.properties")把需要的信息加载进来,在里面再使用参数占位符${}的方式加载信息,再加上一个@bean放入容器
ps:下面运行测试时发现不对劲,不能使用@PropertySource注解加载properties文件
上面第二步的依赖注入的流程图大概就是
第三步
将扫包的MapperScannerConfigurer也加入容器进行bean管理,对于需要mapper接口的路径也一起作为参数给它
最后就是SpringConfig里面所有的信息
ps:经过下面测试,三个方法都要加上static
@Configuration
@ComponentScan(value = {"org.example"})
@PropertySource("db.properties")
public class SpringConfig {
@Bean
public static SqlSessionFactoryBean sqlSessionFactoryBean() throws IOException {
SqlSessionFactoryBean sbean=new SqlSessionFactoryBean();
sbean.setTypeAliasesPackage("org.example.pojo");
sbean.setDataSource(druidDataSource());
sbean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
return sbean;
}
@Bean
public static DruidDataSource druidDataSource(){
DruidDataSource dataSource=new DruidDataSource();
dataSource.setPassword("${jdbc.password}");
dataSource.setUsername("${jdbc.username}");
dataSource.setUrl("${jdbc.url}");
dataSource.setDriverClassName("${jdbc.driver}");
return dataSource;
}
@Bean
public static MapperScannerConfigurer mapperScannerConfigurer(){
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
mapperScannerConfigurer.setBasePackage("org.example.dao");
return mapperScannerConfigurer;
}
}
测试注解Spring整合mybatis的功能
测试里面出现了两个大的错误
第一个错误
Exception in thread "main" org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection; nested exception is java.sql.SQLException: ${jdbc.driver}
### The error may exist in file [F:\acwing——project\spring\tmp\mybatis_demo\spring02mybatis\target\classes\mapper\StudentDao.xml]
### The error may involve org.example.dao.StudentDao.selectAll
### The error occurred while executing a query
主要是Failed to obtain JDBC Connection; nested exception is java.sql.SQLException: ${jdbc.driver}
经过debug发现不知为什么不能使用
@PropertySource(value = {"db.properties"})
的方式引入properties文件后使用参数占位符,只能使用字符串的方式
这是正确写法
第二个错误
该错误不会影响操作执行
Cannot enhance @Configuration bean definition 'springConfig' since its singleton instance has been created too early.
无法增强 Bean 定义 'springConfig@Configuration因为它的单例实例创建得太早了。
经过查证是因为原因是当获取SqlSessionFactoryBean接口的bean时,会调用SqlSessionFactoryBean()方法创建Bean,而调用该方法前同样需要先实例化SpringConfig,导致了SpringConfig在被增强之前被实例化了, 而如果把方法修饰为static,static方法属于类方法,就不会触发SpringConfig被提前实例化也就不会出现警告信息了.
在SpringConfig中的三个方法都是这种类型,所以都要加上static。