前言
在现代软件开发中,数据持久化是一个重要的环节。为了高效、可维护地管理和操作数据库,许多开发者采用了Spring框架和Mybatis持久化框架的组合。Spring提供了依赖注入和面向切面编程等特性,而Mybatis则是一个优秀的对象关系映射(ORM)框架,能够有效地管理数据库与Java对象之间的转换。
本文将介绍如何使用Spring整合Mybatis来简化数据持久化的过程。首先,我们将简单介绍Spring框架和Mybatis框架的基本概念和原理,接着详细探讨Spring和Mybatis整合的步骤以及如何配置和使用它们。
通过阅读本文,您将了解到Spring整合Mybatis的优势和核心功能,以及如何利用它们来提高开发效率和降低代码的复杂度。无论您是初学者还是有一定经验的开发者,本文都会为您提供实用的指导和最佳实践。
一、为什么要使用 Spring 整合 Mybatis
使用Spring整合Mybatis有以下几个重要原因:
简化配置和管理:Spring框架提供了依赖注入和控制反转(IoC)的机制,将对象的创建和依赖关系的管理交给Spring容器处理。通过Spring的配置文件,我们可以轻松地配置Mybatis的数据源、事务管理器等,并将Mapper对象注入到Spring容器中。这样一来,我们不再需要手动管理对象的创建和依赖关系,大大简化了配置和管理的工作。
提供声明式事务管理:Spring框架为数据库事务管理提供了强大的支持。通过Spring整合Mybatis,我们可以使用Spring的事务管理器来管理数据库事务,实现对数据的一致性和完整性的保护。通过声明式的方式,我们可以以简洁、可维护的方式定义事务的边界,而无需手动编写繁琐的事务管理代码。
实现面向切面编程:Spring框架的另一个重要特性是面向切面编程(AOP)。通过AOP,我们可以在系统的不同层次上进行横切关注点的处理,如日志记录、异常处理、性能监控等。Spring整合Mybatis使得我们可以利用AOP机制来实现对持久层的横切关注点处理,例如对SQL语句的日志记录和缓存管理等,极大地提高了系统的灵活性和可维护性。
促进代码的模块化和可重用性:通过Spring整合Mybatis,我们可以将数据访问层的逻辑封装在Mapper接口中,并使用Spring的依赖注入将Mapper对象注入到服务层中。这样一来,我们可以更好地实现代码的模块化和组件化,进一步提高代码的可重用性和可测试性。此外,Mybatis本身也提供了很多数据库操作的常用功能,如条件查询、分页查询、动态SQL等,可以让我们更加高效地编写数据访问层的代码。
综上所述,使用Spring整合Mybatis可以简化配置和管理、提供声明式事务管理、实现面向切面编程,同时也促进了代码的模块化和可重用性。这使得开发人员可以更加专注于业务逻辑的实现,提高开发效率和软件质量。
在后面我也会通过一个 spring 整合 Mybatis 的案例来加入事务管理!
二、开始学习
1、新建项目,结构如下
2、添加依赖
<dependencies>
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.15</version>
</dependency>
<!-- spring 的核心依赖 -->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.23</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.23</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<!-- Mybatis 依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
</dependencies>
这些依赖主要是用于构建一个基于Spring和Mybatis的Java项目,实现与数据库的交互和管理。
com.alibaba:druid
:Druid是一种开源的关系型数据库连接池,提供了比传统连接池更强大的功能和性能监控。通过引入这个依赖,我们可以使用Druid来管理数据库连接。
org.springframework:spring-context
:Spring框架的核心依赖之一,提供了IoC(控制反转)和DI(依赖注入)等核心功能。通过引入这个依赖,我们可以使用Spring来管理应用程序的组件和依赖关系。
org.springframework:spring-jdbc
:Spring框架提供的数据访问层的依赖,包含了与JDBC相关的类和接口。通过引入这个依赖,我们可以使用Spring提供的JDBC支持进行数据库访问和操作。
ch.qos.logback:logback-classic
:Logback是一个灵活的日志框架,可以替代传统的Log4j。logback-classic
是Logback的经典实现,提供了日志记录的功能。通过引入这个依赖,我们可以使用Logback来记录应用程序的日志。
mysql:mysql-connector-java
:MySQL数据库的Java驱动程序,通过引入这个依赖,我们可以连接和操作MySQL数据库。
org.projectlombok:lombok
:Lombok是一个Java库,可以通过注解来自动生成代码,减少重复的样板代码。通过引入这个依赖,我们可以简化Java类的定义和编写。
junit:junit
:JUnit是一个用于编写单元测试的框架。通过引入这个依赖(scope设置为test),我们可以使用JUnit来编写和运行单元测试。
org.mybatis:mybatis
:MyBatis框架的核心依赖,用于数据库访问和映射。详细信息请参考前面的回答。
org.mybatis:mybatis-spring
:MyBatis与Spring框架整合的依赖,提供了MyBatis与Spring集成的支持。详细信息请参考前面的回答。
这些依赖一起使用,可以构建一个基于Spring和MyBatis的Java项目,并实现数据库访问和管理的功能。请注意检查版本号,确保使用的版本兼容并与其他相关依赖项一致。
3、创建数据库表
链接: 百度网盘 请输入提取码 提取码: fmq9 复制这段内容后打开百度网盘手机App,操作更方便哦
由于数据量太大了,只能上传到网盘上,大家去拷贝就可以了。
4、在 entity 包下新建一个实体类 City
@Data
public class City {
private String cityId;
private String cityName;
private String cityCode;
private String province;
}
5、在 dao 包下新建一个 CityDao 接口
public interface CityDao {
/**
* 根据省份查询
* @param province
* @return
*/
List<City> list(String province);
}
这次演示的案例是,根据条件查询城市信息。
6、在 service 包下新建一个 CityService 接口,在 impl 包下新建一个 CityServiceImpl 实现类
CtiytService 接口
public interface CityService {
List<City> listCity(String province);
}
CityServiceImpl 实现类
@Service
@Slf4j
@RequiredArgsConstructor
public class CityServiceImpl implements CityService {
/**
* 这里注入的是代理对象
*/
private final CityDao cityDao;
@Override
public List<City> listCity(String province) {
return cityDao.list(province);
}
}
这段代码是一个基于Spring框架的Java类,其中
@Service
注解表示这个类是一个服务组件,会被Spring容器管理。@Slf4j
注解则表示引入了Lombok库,可以使用log
对象进行日志记录。
@RequiredArgsConstructor
注解是Lombok库提供的注解,它会自动生成一个构造方法,参数为类中已经声明的final
成员变量,对于这段代码中的cityDao
,它会在构造方法中被赋值。
CityServiceImpl
类实现了CityService
接口,并覆写了listCity
方法。在这个方法中,调用cityDao.list(province)
方法获取数据并返回。这个方法实际上是委托给另一个组件CityDao
来完成的,因为cityDao
是通过构造函数注入进来的。由于
cityDao
是一个代理对象,而非CityDao
接口原本的实现类,在运行时会动态生成一个代理实现,实现了对CityDao
接口的方法调用拦截和增强等功能,以便实现事务管理、缓存等功能。
7、在 resources 包下新建一个db.properties文件,在mappers包下新建一个CityMapper.xml
db.properties 文件
driver = com.mysql.cj.jdbc.Driver
url = jdbc:mysql://localhost:3306/psm
username= root
password = 123456
maxActive = 200
initialSize = 5
minIdle = 5
maxWait = 2000
minEvictableIdleTimeMillis = 300000
timeBetweenEvictionRunsMillis = 60000
testWhileIdle = true
testOnReturn = false
validationQuery = select 1
poolPreparedStatements = false
这些属性是用于配置Druid连接池的相关参数,具体含义如下:
- driver:数据库驱动类的全限定名,这里指定使用MySQL数据库的驱动。
- url:数据库连接的URL地址,指向本地MySQL数据库。设置了useUnicode=true和characterEncoding=utf-8,表示使用UTF-8字符集编码。
- name:连接数据库所需的用户名,这里设置为"root"。注意:这里本来是 username ,为什么要改成 name 呢?因为写 username 的话会识别成本机的用户名,所以这里要改成 name.
- password:连接数据库所需的密码,这里设置为"123456"。
- maxActive:连接池中同时可从数据源获取的最大活跃连接数,这里设置为200,即同时最多容纳200个连接。
- initialSize:初始化时创建的连接数,这里设置为5。
- minIdle:连接池中保持的最小空闲连接数,这里设置为5。
- maxWait:当连接池没有可用连接时,等待获取连接的最大时间(单位:毫秒),这里设置为2000毫秒。
- minEvictableIdleTimeMillis:连接在池中最小空闲时间(单位:毫秒),这里设置为300000毫秒(即5分钟)。
- timeBetweenEvictionRunsMillis:间隔多久进行一次空闲连接的检测(单位:毫秒),这里设置为60000毫秒(即1分钟)。
- testWhileIdle:在连接空闲时,是否执行validationQuery检测连接是否有效,这里设置为true。
- testOnReturn:在归还连接到连接池时,是否执行validationQuery检测连接是否有效,这里设置为false。
- validationQuery:用于检测连接是否有效的SQL查询语句,这里设置为"select 1"。
- poolPreparedStatements:是否缓存PreparedStatement(预编译的SQL语句),这里设置为false。
通过配置这些属性,可以更好地管理和优化Druid连接池的使用。可以指定数据库连接的URL、用户名和密码,设置连接池的大小、最大活跃连接数、空闲连接数等参数,以及进行连接的有效性校验和空闲连接的检测等。
注意:这些属性不能写错,写错了就不会生效了,会报错。
CityMapper.xml
<?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="edu.nf.ch02.dao.CityDao">
<resultMap id="cityMap" type="edu.nf.ch02.entity.City">
<id property="cityId" column="city_id"/>
<result property="cityName" column="city_name"/>
<result property="cityCode" column="city_code"/>
<result property="province" column="province"/>
</resultMap>
<select id="list" parameterType="string" resultMap="cityMap">
select city_id,city_name,city_code,province
from city_info where province = #{province}
</select>
</mapper>
这是一个MyBatis的映射文件(mapper),用于定义数据库操作的SQL语句和结果映射规则。
在这个映射文件中,首先声明了一个命名空间
namespace
为edu.nf.ch02.dao.CityDao
,这个命名空间和Java接口CityDao
对应。这个接口定义了与数据库交互的方法。接着定义了一个
resultMap
标签,id 为cityMap
,type 为edu.nf.ch02.entity.City
,表示将查询结果映射到City
类。在resultMap
中定义了四个属性映射关系,分别为cityId
、cityName
、cityCode
和province
,对应数据库表中的列名。最后是一个
select
标签,id 为list
,参数类型为String
,结果映射使用了之前定义的cityMap
。SQL语句为select city_id, city_name, city_code, province from city_info where province = #{province}
,其中#{province}
是一个占位符,表示传入的参数值。通过这个映射文件,MyBatis 就知道如何执行查询操作,并将查询结果映射到
City
类的对象中。这样,在使用CityDao
接口中的方法时,Mybatis会根据映射文件中的定义来执行相应的SQL语句并返回结果。
8、在 config 包下新建一个 AppConfig 配置类
@Configuration
// 扫描 dao 接口的包,动态生成 dao 接口的代理实现
// 等效于 xml 中的 <mybatis:scan>
@MapperScan(basePackages = "edu.nf.ch02.dao")
@ComponentScan(basePackages = "edu.nf.ch02")
public class AppConfig {
/**
* 装配 dataSource 数据源
*
* @return
* @throws Exception
*/
@Bean(initMethod = "init", destroyMethod = "close")
public DruidDataSource dateSource() throws Exception {
// 创建 Properties 对象
Properties prop = new Properties();
// 获取一个输入流来读取 properties 文件
InputStream input = AppConfig.class.getClassLoader().getResourceAsStream("db.properties");
// 将输入流加载到 properties 对象中
prop.load(input);
// 通过 DruidDataSourceFactory 来创建 DruidDataSource 实例
DruidDataSource ds = (DruidDataSource) DruidDataSourceFactory.createDataSource(prop);
return ds;
}
/**
* 整合 mybatis ,装配 SqlSessionFactory
*
* @return
*/
@Bean
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) throws Exception {
// 创建 SqlSessionFactoryBean
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
// 注入数据源
sqlSessionFactoryBean.setDataSource(dataSource);
// 注入 mybatis 其他属性
// 设置包的别名
sqlSessionFactoryBean.setTypeAliasesPackage("edu.nf.ch02.entity");
// mapper 映射配置文件的路径
// 先创建一个资源路径解析器来查找源文件
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("classpath:mappers/*.xml");
sqlSessionFactoryBean.setMapperLocations(resources);
// 返回 SqlSessionFactoryBean 给容器
return sqlSessionFactoryBean;
}
}
这是一个Spring的配置类
AppConfig
,用于配置整合 MyBatis 和数据源。首先使用注解
@MapperScan
扫描包edu.nf.ch02.dao
下的接口,动态生成接口的代理实现类。相当于在XML中使用<mybatis:scan>
配置。然后使用注解
@ComponentScan
扫描包edu.nf.ch02
,将标有@Component
、@Service
、@Repository
、@Controller
等注解的类纳入Spring容器管理。下面定义了一个名为
dataSource
的Bean方法,返回一个DruidDataSource
实例,该方法通过读取db.properties
文件中的配置来创建数据源。使用了@Bean
注解,同时指定了初始化方法init
和销毁方法close
。接着定义了一个名为
sqlSessionFactory
的Bean方法,返回一个SqlSessionFactoryBean
实例。该方法需要传入数据源dataSource
。在方法中,创建了SqlSessionFactoryBean
对象,并设置了数据源和其他一些属性,比如设置了实体类的别名包路径和映射文件的位置。使用了@Bean
注解。这样,在Spring容器启动时,会根据配置生成数据源和SqlSessionFactory,并注册到容器中供其他组件使用。
9、测试
@Slf4j
public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 从容器中获取动态创建的 CityDao 的代理对象
CityService cityDao = context.getBean(CityService.class);
List<City> list = cityDao.listCity("广东");
list.forEach(city -> log.info(city.getCityName()));
}
}
运行结果
在
main
方法中,创建了一个ApplicationContext
对象,并指定使用AnnotationConfigApplicationContext
类来加载应用程序配置类AppConfig
。这样就会根据AppConfig
中的配置信息创建并配置Spring容器。接下来,通过容器对象
context
的getBean
方法,传入CityService.class
参数来获取动态生成的CityDao
的代理对象。这里假设CityService
是CityDao
接口的实现类。然后,调用
cityDao
的方法listCity("广东")
查询城市列表,并将结果保存到list
集合中。最后,使用
forEach
方法遍历list
集合中的每个城市对象,并使用日志记录器log
打印城市名称。这样,运行程序时,就会通过Spring容器获取CityDao的代理对象,并使用它查询并打印城市列表的名称。
三、总结
以前我们操作数据库是不是没有这么方便,以前我们要写大量的 dao 实现类的代码去操作(CRUD)数据库,现在使用 spring 整合 Mybatis 是不是不用写 dao 实现类了,是不是很方便。大大节省了开发时间和开发效率,使用框架的好处就体现出来了。
这个案例还没有完全讲完,我将在下一篇博客中继续讲解 spring 整合 Mybatis,下一章讲的就是 spring 整合 Mybatis 分页,使用 pagehelper 实现分页功能。
注意:完成这个案例之前建议先去看看我上一篇博客,spring 整合数据源。
四、gitee 案例
完整代码地址:qiuqiu/conformity-study (gitee.com)