目录
3、 编写实体类、mapper、mapper.xml、service层
Spring为什么要整合Mybatis? 🌹
Spring 和 MyBatis 是两个在 Java 开发中广泛应用的优秀框架。Spring 提供了强大的容器和一系列的特性,可以方便地进行依赖注入、AOP 编程等操作。MyBatis 是一个轻量级的持久层框架,它提供了灵活的 SQL 映射和数据库访问功能。在实际项目中,将 Spring 和 MyBatis 整合在一起使用,可以发挥它们各自的优势,简化开发流程,提高系统的可维护性和扩展性。
整合步骤 ⭐
1、 导入相关的Maven依赖
<!--版本锁定-->
<spring.version>5.2.7.RELEASE</spring.version>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<!-- spring -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<!-- aop -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.9.5</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.0</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
<!-- 整合spring和mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
<!-- utils -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
</dependency>
</dependencies>
2 、 配置Mybatis的文件
- 日志文件 (log4j.properties)
- 数据库配置文件( database.properties )
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/test?serverTimezone=GMT-8&useUnicode=true&character=utf-8
name=root
pwd=root
- 编写mybatis-config.xml 。这时候就不需要再配置数据源,配置数据源就交给我们的Spring,只需要取别名 引入映射文件。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 设置MyBatis框架的运行时行为-->
<settings>
<!--使用Log4j作为MyBatis框架的日志实现-->
<setting name="logImpl" value="LOG4J"/>
<!-- 开启自动映射 -->
<setting name="autoMappingBehavior" value="FULL"/>
</settings>
<!--简写类的名称 自动匹配实体类的名字-->
<typeAliases>
<package name="com.cskt.domain"/>
</typeAliases>
<!--配置需要引用的SQL映射文件的位置-->
<mappers>
<mapper resource="mapper/SysUserMapper.xml"/>
</mappers>
</configuration>
3、 编写实体类、mapper、mapper.xml、service层
package com.cskt.domain;
import lombok.Data;
import org.springframework.stereotype.Component;
import java.io.Serializable;
import java.util.Date;
/**
* @Description TODO
* @Author Yii_oo
* @Date 2023-08-17 21:21
* @Version 1.0
* @ClassDesc 用户实体类
*/
@Data
public class SysUser implements Serializable {
private Integer id; /**ID*/
private String account; /**账号*/
private String password; /**用户密码*/
private String realName; /**真实姓名*/
private Integer sex; /**性别*/
private Date birthday; /**出生日期*/
private String address; /**地址*/
private String phone; /**电话*/
private Integer roleId; /**角色Id*/
private String userRoleName; /**角色名称*/
private Integer createdUserId; /**创建者*/
private Date createdTime; /**创建时间*/
private Integer updatedUserId; /**更新者*/
private Integer updatedTime; /**更新时间*/
}
<?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.cskt.mapper.SysUserMapper">
<select id="searchAll" resultType="SysUser">
select * from t_sysuser
</select>
</mapper>
package com.cskt.mapper;
import com.cskt.domain.SysUser;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository("sysUserMapper") //可能会报错,还没有配置
public interface SysUserMapper {
/**
* 查询所有用户信息
* @return
*/
List<SysUser> searchAll();
}
package com.cskt.service;
import com.cskt.domain.SysUser;
import java.util.List;
public interface SysUserService {
/**
* 查询所有用户信息
* @return
*/
List<SysUser> searchAll();
}
package com.cskt.service.impl;
import com.cskt.domain.SysUser;
import com.cskt.mapper.SysUserMapper;
import com.cskt.service.SysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import java.util.List;
@Service("userService")
public class SysUserServiceImpl implements SysUserService {
@Autowired //自动装配
//如果装配不进
@Qualifier("sysUserMapper")
private SysUserMapper sysUserMapper;
@Override
public List<SysUser> searchAll() {
return sysUserMapper.searchAll();
}
}
4、建立spring配置文件
- applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 1、引入 properties 配置文件 --> <context:property-placeholder location="database.properties" /> <!-- 2、配置数据源 --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="${driver}" /> <property name="url" value="${url}" /> <property name="username" value="${name}" /> <property name="password" value="${pwd}" /> </bean> <!-- 3、创建SqlSessionFactory --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 绑定数据源 --> <property name="dataSource" ref="dataSource" /> <!-- 指定Mybatis配置文件路径 --> <property name="configLocation" value="mybatis-config.xml" /> </bean> <!-- 4、MapperScannerConfig 将指定包下的 Mapper 接口注册到 Spring 容器中。 我们之前是通过<bean>元素中的 class属性 去扫描我们的MapperImpl(类), 而现在我们没有Impl类了,使用<bean>元素中的 class属性是会报错的 所以我们才会需要配置MapperScannerConfig,这样我们的@Repository注解就可以在Mapper里直接使用了 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.cskt.mapper" /> </bean> <!-- 5、注解实现依赖注入 --> <context:component-scan base-package="com.cskt.service" /> </beans>
BasicDataSource
BasicDataSource
是一个基于 Apache Commons DBCP(Database Connection Pool)的数据源实现。它是 Spring 提供的一个用于管理和获取数据库连接的类。具体来说,BasicDataSource
是 Spring 对于 Apache Commons DBCP 连接池的一种封装。
它的源代码里面封装了很多的连接属性和方法。
- 用于设置连接初始化 SQL。当使用连接池获取连接时,每次新建的连接会先执行 ConnectionInitSqls 中配置的 SQL 语句。
public void setConnectionInitSqls(Collection connectionInitSqls) {
if (connectionInitSqls != null && connectionInitSqls.size() > 0) {
ArrayList newVal = null;
Iterator iterator = connectionInitSqls.iterator();
while(iterator.hasNext()) {
Object o = iterator.next();
if (o != null) {
String s = o.toString();
if (s.trim().length() > 0) {
if (newVal == null) {
newVal = new ArrayList();
}
newVal.add(s);
}
}
}
this.connectionInitSqls = newVal;
} else {
this.connectionInitSqls = null;
}
this.restartNeeded = true;
}
SqlSessionFactoryBean
这个sqlSessionFactory的实现类中做了什么事情。首先看看这个类的声明:
public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
//省略代码
}
从配置来看,这个Bean至少提供了两个方法,一个是和dataSource有关,一个和mapperLocations有关
public void setDataSource(DataSource dataSource) {
if (dataSource instanceof TransactionAwareDataSourceProxy) {
this.dataSource = ((TransactionAwareDataSourceProxy)dataSource).getTargetDataSource();
} else {
this.dataSource = dataSource;
}
}
public void setMapperLocations(Resource[] mapperLocations) {
this.mapperLocations = mapperLocations;
}
- 使用config构建sqlSessionFactory实例。然后MapperLocations配置的Mapper文件和addMapper文件和方法添加到SqlSession的配置中
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
XMLConfigBuilder xmlConfigBuilder = null;
Configuration configuration;
if (this.configuration != null) {
configuration = this.configuration;
if (configuration.getVariables() == null) {
configuration.setVariables(this.configurationProperties);
} else if (this.configurationProperties != null) {
configuration.getVariables().putAll(this.configurationProperties);
}
} else if (this.configLocation != null) {
xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), (String)null, this.configurationProperties);
configuration = xmlConfigBuilder.getConfiguration();
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Property `configuration` or 'configLocation' not specified, using default MyBatis Configuration");
}
configuration = new Configuration();
configuration.setVariables(this.configurationProperties);
}
if (this.objectFactory != null) {
configuration.setObjectFactory(this.objectFactory);
}
if (this.objectWrapperFactory != null) {
configuration.setObjectWrapperFactory(this.objectWrapperFactory);
}
if (this.vfs != null) {
configuration.setVfsImpl(this.vfs);
}
String[] typeHandlersPackageArray;
String[] var4;
int var5;
int var6;
String packageToScan;
if (StringUtils.hasLength(this.typeAliasesPackage)) {
typeHandlersPackageArray = StringUtils.tokenizeToStringArray(this.typeAliasesPackage, ",; \t\n");
var4 = typeHandlersPackageArray;
var5 = typeHandlersPackageArray.length;
for(var6 = 0; var6 < var5; ++var6) {
packageToScan = var4[var6];
configuration.getTypeAliasRegistry().registerAliases(packageToScan, this.typeAliasesSuperType == null ? Object.class : this.typeAliasesSuperType);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases");
}
}
}
int var27;
if (!ObjectUtils.isEmpty(this.typeAliases)) {
Class[] var25 = this.typeAliases;
var27 = var25.length;
for(var5 = 0; var5 < var27; ++var5) {
Class<?> typeAlias = var25[var5];
configuration.getTypeAliasRegistry().registerAlias(typeAlias);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered type alias: '" + typeAlias + "'");
}
}
}
if (!ObjectUtils.isEmpty(this.plugins)) {
Interceptor[] var26 = this.plugins;
var27 = var26.length;
for(var5 = 0; var5 < var27; ++var5) {
Interceptor plugin = var26[var5];
configuration.addInterceptor(plugin);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered plugin: '" + plugin + "'");
}
}
}
if (StringUtils.hasLength(this.typeHandlersPackage)) {
typeHandlersPackageArray = StringUtils.tokenizeToStringArray(this.typeHandlersPackage, ",; \t\n");
var4 = typeHandlersPackageArray;
var5 = typeHandlersPackageArray.length;
for(var6 = 0; var6 < var5; ++var6) {
packageToScan = var4[var6];
configuration.getTypeHandlerRegistry().register(packageToScan);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers");
}
}
}
if (!ObjectUtils.isEmpty(this.typeHandlers)) {
TypeHandler[] var28 = this.typeHandlers;
var27 = var28.length;
for(var5 = 0; var5 < var27; ++var5) {
TypeHandler<?> typeHandler = var28[var5];
configuration.getTypeHandlerRegistry().register(typeHandler);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered type handler: '" + typeHandler + "'");
}
}
}
if (this.databaseIdProvider != null) {
try {
configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
} catch (SQLException var24) {
throw new NestedIOException("Failed getting a databaseId", var24);
}
}
if (this.cache != null) {
configuration.addCache(this.cache);
}
if (xmlConfigBuilder != null) {
try {
xmlConfigBuilder.parse();
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'");
}
} catch (Exception var22) {
throw new NestedIOException("Failed to parse config resource: " + this.configLocation, var22);
} finally {
ErrorContext.instance().reset();
}
}
if (this.transactionFactory == null) {
this.transactionFactory = new SpringManagedTransactionFactory();
}
configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));
if (!ObjectUtils.isEmpty(this.mapperLocations)) {
Resource[] var29 = this.mapperLocations;
var27 = var29.length;
for(var5 = 0; var5 < var27; ++var5) {
Resource mapperLocation = var29[var5];
if (mapperLocation != null) {
try {
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(), configuration, mapperLocation.toString(), configuration.getSqlFragments());
xmlMapperBuilder.parse();
} catch (Exception var20) {
throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", var20);
} finally {
ErrorContext.instance().reset();
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");
}
}
}
} else if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
}
return this.sqlSessionFactoryBuilder.build(configuration);
}
MapperScannerConfigure
MapperScannerConfigurer
是一个用于扫描和注册 MyBatis Mapper 接口的类。它可以自动将 Mapper 接口转化为 MyBatis 的映射器,并将其注入到 Spring 容器中供应用程序使用。
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {
this.processPropertyPlaceHolders();
}
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
scanner.setAddToConfig(this.addToConfig);
scanner.setAnnotationClass(this.annotationClass);
scanner.setMarkerInterface(this.markerInterface);
scanner.setSqlSessionFactory(this.sqlSessionFactory);
scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
scanner.setResourceLoader(this.applicationContext);
scanner.setBeanNameGenerator(this.nameGenerator);
scanner.registerFilters();
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n"));
}
postProcessBeanDefinitionRegistry()
方法是在 Spring 初始化过程中执行的回调方法,用于对 BeanDefinitionRegistry 进行后处理。其中,MapperScannerConfigurer
会创建一个扫描器对象,并在指定的包路径下扫描 Mapper 接口,将其注册为 Spring Bean。这样,在应用程序中就可以使用自动注入等方式来使用这些 Mapper 接口。
5、测试
package com.cskt.service;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import static org.junit.Assert.*;
public class SysUserServiceTest {
@Test
public void searchAll() {
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
SysUserService userService =(SysUserService) context.getBean("userService");
System.out.println(userService.searchAll());
}
}
注:只要没报错,能输出结果就整合成功了!
可能会出现的报错 🎂
- userService没注入进去,只需要看注解和扫描,看看是不是哪里编写错误。
解决办法:看看是不是注解里面的命名不正确,或者扫描没配置正确!
- 这个问题一般很难找出来,只需要看数据库配置文件 (database.properties)。这个问题呢,就是你的命名和他配置数据源的关键字重复、冲突了!!
解决办法:更改命名!不更配置数据源的关键字重名就行了,或者前面加个jdbc. 的前缀(jdbc.usernme)