学习总结录 学习总结录
首页
归档
分类
标签
  • Java基础
  • Java集合
  • MySQL
  • Redis
  • JVM
  • 多线程
  • 计算机网络
  • 操作系统
  • Spring
  • Kafka
  • Elasticsearch
  • Python
  • 面试专题
  • 案例实践
  • 工具使用
  • 项目搭建
  • 服务治理
  • ORM框架
  • 分布式组件
  • MiniSpring
  • 设计模式
  • 算法思想
  • 编码规范
友链
关于
GitHub (opens new window)
首页
归档
分类
标签
  • Java基础
  • Java集合
  • MySQL
  • Redis
  • JVM
  • 多线程
  • 计算机网络
  • 操作系统
  • Spring
  • Kafka
  • Elasticsearch
  • Python
  • 面试专题
  • 案例实践
  • 工具使用
  • 项目搭建
  • 服务治理
  • ORM框架
  • 分布式组件
  • MiniSpring
  • 设计模式
  • 算法思想
  • 编码规范
友链
关于
GitHub (opens new window)
  • 案例实践

  • 工具使用

  • 项目搭建

  • 服务治理

  • ORM框架

    • 简易ORM框架
    • ORM整合Spring
      • 一、需求背景
      • 二、方案设计
      • 三、实现方案
        • 1、依赖注入
        • 2、SqlSession工厂实例注入
        • 3、Mapper扫描类
        • 4、代理类
      • 四、案例测试
        • 1、配置文件
        • 2、Bean和Mapper
        • 3、接口测试
      • 参考
  • MiniSpring

  • 案例归档
  • ORM框架
旭日
2023-04-24
目录

ORM整合Spring

# 一、需求背景

先前我们实现了一个mini-mybatis,通过对配置文件的加载,XML映射、JDBC的封装,实现了对数据的CRUD操作。现在,我们要将这个简易的ORM框架注入到Spring中,把框架的管理交给Spring容器。

# 二、方案设计

对于我们的ORM框架,需要把一些诸如mapper的扫描、SqlSession工厂创建等交给Spring容器中,由Spring容器进行管理。

  • 在方案设计中包括:需要注册对象的扫描、代理类的实现、Bean的注册
  • 通过SqlSessionFactoryBuilder创建连接工厂,连接到ORM框架。

# 三、实现方案

# 1、依赖注入

需要将先前我们实现的简易ORM框架引入进来:

    <dependency>
        <groupId>me.xu</groupId>
        <artifactId>spring-mini-mybatis</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>

# 2、SqlSession工厂实例注入

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean {

    private String resource;
    private SqlSessionFactory sqlSessionFactory;

    @Override
    public SqlSessionFactory getObject() throws Exception {
        return sqlSessionFactory;
    }

    @Override
    public Class<?> getObjectType() {
        return SqlSessionFactory.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        try (Reader reader = Resources.getResourceAsReader(resource)) {
            this.sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void setResource(String resource) {
        this.resource = resource;
    }


}

该类主要用于mybatis的核心加载,比如资源、SqlSessionFactory。

  • afterPropertiesSet:调用SqlSessionFactoryBuilder的构造方法

# 3、Mapper扫描类

public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor {

    private String basePackage;

    private SqlSessionFactory sqlSessionFactory;

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        try {
            String packageSearchPath = "classpath*:" + basePackage.replace('.', '/') + "/*.class";
            ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
            Resource[] resources = resourcePatternResolver.getResources(packageSearchPath);

            for (Resource resource : resources) {
                MetadataReader metadataReader = new SimpleMetadataReader(resource, ClassUtils.getDefaultClassLoader());

                ScannedGenericBeanDefinition beanDefinition = new ScannedGenericBeanDefinition(metadataReader);
                String beanName = Introspector.decapitalize(ClassUtils.getShortName(beanDefinition.getBeanClassName()));

                beanDefinition.setResource(resource);
                beanDefinition.setSource(resource);
                beanDefinition.setScope("singleton");
                beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(beanDefinition.getBeanClassName());
                beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(sqlSessionFactory);
                beanDefinition.setBeanClass(MapperFactoryBean.class);

                BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(beanDefinition, beanName);
                registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }

    public void setBasePackage(String basePackage) {
        this.basePackage = basePackage;
    }

    public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
        this.sqlSessionFactory = sqlSessionFactory;
    }

}
public class SimpleMetadataReader implements MetadataReader {
    private final Resource resource;

    private final ClassMetadata classMetadata;

    private final AnnotationMetadata annotationMetadata;

    public SimpleMetadataReader(Resource resource, ClassLoader classLoader) throws IOException {
        ClassReader classReader;
        try (InputStream is = new BufferedInputStream(resource.getInputStream())) {
            classReader = new ClassReader(is);
        } catch (IllegalArgumentException ex) {
            throw new NestedIOException("ASM ClassReader failed to parse class file - " +
                    "probably due to a new Java class file version that isn't supported yet: " + resource, ex);
        }

        AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor(classLoader);
        classReader.accept(visitor, ClassReader.SKIP_DEBUG);

        this.annotationMetadata = visitor;
        // (since AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor)
        this.classMetadata = visitor;
        this.resource = resource;
    }

    @Override
    public Resource getResource() {
        return this.resource;
    }

    @Override
    public ClassMetadata getClassMetadata() {
        return this.classMetadata;
    }

    @Override
    public AnnotationMetadata getAnnotationMetadata() {
        return this.annotationMetadata;
    }


}

在设置了基本路径之后,就需要把所有的Mapper接口扫描出来,完成代理和注册到Spring Bean容器里面。

  • 根据基础路径,扫描到下面所有资源
  • 遍历资源,获取到class信息,用于注册bean

# 4、代理类

public class MapperFactoryBean<T> implements FactoryBean<T> {

    private Logger logger = LoggerFactory.getLogger(MapperFactoryBean.class);

    private Class<T> mapperInterface;

    private SqlSessionFactory sqlSessionFactory;

    public MapperFactoryBean(Class<T> mapperInterface, SqlSessionFactory sqlSessionFactory) {
        this.mapperInterface = mapperInterface;
        this.sqlSessionFactory = sqlSessionFactory;
    }

    @Override
    public T getObject() throws Exception {
        InvocationHandler handler = (proxy, method, args) -> {
            logger.info("你被代理了,执行SQL操作!{}", method.getName());
            if ("toString".equals(method.getName())) {
                return null; // 排除Object方法
            }
            try {
                return sqlSessionFactory.openSession().selectOne(mapperInterface.getName() + "." + method.getName(), args[0]);
            } catch (Exception e) {
                e.printStackTrace();
            }

            return method.getReturnType().newInstance();
        };
        return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{mapperInterface}, handler);
    }

    @Override
    public Class<?> getObjectType() {
        return mapperInterface;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

}

getObject()是一个Java代理类的实现,这个代理类对象会挂到注入中,真正调用方法内容时候,会执行到代理类的实现部分。

# 四、案例测试

# 1、配置文件

数据源配置

<?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>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/test?useUnicode=true"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="mapper/Student_Mapper.xml"/>
    </mappers>

</configuration>

Spring Bean配置

<?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: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-3.0.xsd     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
       default-autowire="byName">
    <context:component-scan base-package="me.xu"/>

    <aop:aspectj-autoproxy/>

    <bean id="sqlSessionFactory" class="me.xu.mybatis.SqlSessionFactoryBean">
        <property name="resource" value="mybatis-config-datasource.xml"/>
    </bean>

    <bean class="me.xu.mybatis.MapperScannerConfigurer">
        <!-- 给出需要扫描Dao接口包 -->
        <property name="basePackage" value="me.xu.mybatis.mapper"/>
        <!-- 注入sqlSessionFactory -->
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>

</beans>

# 2、Bean和Mapper

Bean

public class Student {

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    private String id;

    private String name;

    private Integer age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

mapper

public interface StudentMapper {

    Student queryStuInfoByName(String name);

}
<?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="me.xu.mybatis.mapper.StudentMapper">


    <select id="queryStuInfoByName" parameterType="java.lang.String" resultType="me.xu.mybatis.bean.Student">
        SELECT id, name, age
        FROM student
        where name = #{name}
    </select>


</mapper>

# 3、接口测试

public class ApiTest {

    @Test
    public void queryStuInfoByName() {
        BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring-config.xml");
        StudentMapper studentMapper = beanFactory.getBean("studentMapper", StudentMapper.class);
        Student student = studentMapper.queryStuInfoByName("学生02");
        System.out.println(student);
    }

}

# 参考

ORM框架与Spring整合 (opens new window)

上次更新: 2024/06/29, 15:13:44
简易ORM框架
Spring IoC-原始Ioc

← 简易ORM框架 Spring IoC-原始Ioc→

最近更新
01
基础概念
10-31
02
Pytorch
10-30
03
Numpy
10-30
更多文章>
Theme by Vdoing | Copyright © 2021-2024 旭日 | 蜀ICP备2021000788号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式