Spring 整合 Mybatis

目录

Spring为什么要整合Mybatis?  🌹

整合步骤 ⭐

 1、 导入相关的Maven依赖

 2 、 配置Mybatis的文件

  3、  编写实体类、mapper、mapper.xml、service层

  4、建立spring配置文件

 BasicDataSource 

SqlSessionFactoryBean

MapperScannerConfigure

5、测试

  可能会出现的报错 🎂


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)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值