小例Java结合Spring框架和MyBatis ORM来实现 ERP项目中实现读写分离

news2025/1/20 8:32:14

前记:大家带着挑剔的眼光,多多批判和指正!🙏

在ERP项目中实现读写分离,我们可以使用Java结合Spring框架和MyBatis ORM来实现。以下是一个简化的例子,展示了如何在ERP项目中配置和使用读写分离。

一、项目结构

假设我们的ERP项目使用Maven构建,项目结构大致如下:

erp-project
├── src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │       └── example
│   │   │           ├── config
│   │   │           │   └── DataSourceConfig.java
│   │   │           ├── mapper
│   │   │           │   └── ProductMapper.java
│   │   │           ├── model
│   │   │           │   └── Product.java
│   │   │           ├── service
│   │   │           │   └── ProductService.java
│   │   │           └── service/impl
│   │   │               └── ProductServiceImpl.java
│   │   └── resources
│   │       ├── application.yml
│   │       └── mapper
│   │           └── ProductMapper.xml
│   └── test
│       └── java
│           └── com
│               └── example
│                   └── ErpProjectApplicationTests.java
├── pom.xml
└── README.md


二、配置数据源

application.yml中配置主从数据源:

spring:
  datasource:
    master:
      url: jdbc:mysql://localhost:3306/erp_master
      username: root
      password: root
      driver-class-name: com.mysql.cj.jdbc.Driver
    slave:
      url: jdbc:mysql://localhost:3306/erp_slave
      username: root
      password: root
      driver-class-name: com.mysql.cj.jdbc.Driver

mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.example.model


三、创建数据源配置类

com.example.config包下创建DataSourceConfig.java,用于配置数据源和动态数据源路由:

// 省略了部分代码,包括导入语句和具体实现细节
@Configuration
public class DataSourceConfig {

    @Bean(name = "masterDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DataSource masterDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "slaveDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource slaveDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Primary
    @Bean(name = "dataSource")
    public DataSource routingDataSource(@Qualifier("masterDataSource") DataSource masterDataSource,
                                        @Qualifier("slaveDataSource") DataSource slaveDataSource) {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceType.MASTER, masterDataSource);
        targetDataSources.put(DataSourceType.SLAVE, slaveDataSource);
        DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource();
        dataSource.setDefaultTargetDataSource(masterDataSource);
        dataSource.setTargetDataSources(targetDataSources);
        return dataSource;
    }

    // 其他配置和动态数据源路由逻辑...
}


四、创建模型类

com.example.model包下创建Product.java

// 省略了导入语句
public class Product {
    private Long id;
    private String name;
    private Double price;
    // getters and setters...
}


五、创建Mapper接口和XML映射文件

com.example.mapper包下创建ProductMapper.java

// 省略了导入语句
@Mapper
public interface ProductMapper {
    @Select("SELECT * FROM product WHERE id = #{id}")
    Product findById(Long id);

    @Insert("INSERT INTO product(name, price) VALUES(#{name}, #{price})")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    void insert(Product product);

    // 其他CRUD方法...
}

resources/mapper目录下创建ProductMapper.xml(如果使用了XML配置):

<!-- 省略了XML声明和DOCTYPE声明 -->
<mapper namespace="com.example.mapper.ProductMapper">
    <!-- SQL语句... -->
</mapper>


注意:在这个例子中,我们实际上没有使用XML映射文件,因为所有的SQL语句都直接在Mapper接口中使用了注解。如果你更喜欢使用XML配置,可以将SQL语句移到ProductMapper.xml中。

六、创建服务类

com.example.servicecom.example.service.impl包下创建服务接口和实现类:

// ProductService.java
public interface ProductService {
    Product findById(Long id);
    void createProduct(Product product);
    // 其他服务方法...
}

// ProductServiceImpl.java
@Service
public class ProductServiceImpl implements ProductService {

    @Autowired
    private ProductMapper productMapper;

    @Override
    @DataSource(DataSourceType.SLAVE) // 读取时使用从库
    public Product findById(Long id) {
        return productMapper.findById(id);
    }

    @Override
    @DataSource(DataSourceType.MASTER) // 写入时使用主库
    public void createProduct(Product product) {
        productMapper.insert(product);
    }

    // 其他服务方法实现...
}

注意:@DataSource注解是一个自定义注解,用于标记方法应该使用哪个数据源。你需要自己实现这个注解和它的处理逻辑(通常通过AOP)。

七、自定义注解和AOP处理

创建DataSource注解和AOP切面来处理数据源切换:

// DataSource.java
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {
    DataSourceType value() default DataSourceType.MASTER;
}

// DataSourceAspect.java
@Aspect
@Component
public class DataSourceAspect {

    @Before("@annotation(dataSource)")
    public void changeDataSource(JoinPoint point, DataSource dataSource) {
        DataSourceContextHolder.setDataSourceType(dataSource.value());
    }

    @After("@annotation(dataSource)")
    public void clearDataSource(JoinPoint point, DataSource dataSource) {
        DataSourceContextHolder.clearDataSourceType();
    }
}


你还需要实现DataSourceContextHolderDataSourceType枚举类来存储和获取当前线程的数据源类型。

八、测试

编写单元测试或启动应用程序,通过调用ProductService的方法来验证读写分离是否按预期工作。

九、注意事项

  1. 确保主从数据库之间的数据同步是及时的。
  2. 在高并发场景下,需要特别注意数据源切换的线程安全性。
  3. 根据业务需求,可能需要更复杂的读写分离策略,比如基于负载均衡的读操作分发。

这个例子提供了一个基本的框架,你可以根据实际需求进行扩展和修改。

(望各位潘安、各位子健不吝赐教!多多指正!🙏)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2279284.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AI News(1/19/2025):Microsoft AutoGen v0.4:走向更智能 AI 代理的转折点

1、Microsoft AutoGen v0.4&#xff1a;走向更智能 AI 代理的转折点[1] 微软发布的AutoGen v0.4是AI代理领域的一个重要进步。该框架采用异步事件驱动架构&#xff0c;使代理能够并行执行任务&#xff0c;提高任务执行速度和资源利用效率&#xff0c;这对于多代理系统尤为重要…

4. LwIP_网络数据包管理

概述 协议栈的本质&#xff1a; TCP/IP协议栈的实现&#xff0c;本质上就是对数据包的管理。在LwIP中&#xff0c;定义了一个pbuf结构体对数据包进行管理。 pbuf管理数据包的步骤&#xff1a; 1、用户产生要传输的数据 2、用户在内存堆/内存池中申请一个pbuf结构体 3、将…

NPC与AI深度融合结合雷鸟X3Pro AR智能眼镜:引领游戏行业沉浸式与增强现实新纪元的畅想

if… NPC&#xff08;非玩家角色&#xff09;与AI&#xff08;人工智能&#xff09;的深度融合&#xff0c;正引领游戏行业迈向一个全新的沉浸式与增强现实&#xff08;AR&#xff09;相结合的新时代。这一创新不仅预示着游戏体验的质变&#xff0c;更可能全面革新游戏设计与叙…

Redis系列之底层数据结构字典Dict

Redis系列之底层数据结构字典Dict Dict数据结构 Dict是Redis数据结构中使用最为频繁的复合型数据结构&#xff0c;本质上是一个哈希表 查看redis6.0版本的源码&#xff0c;链接&#xff1a;https://github.com/redis/redis/blob/6.0/src/dict.h 哈希表的结构定义&#xff1…

前端【3】--CSS布局,CSS实现横向布局,盒子模型

盒子分类 1、块级盒子 2、内联级盒子 3、内联块级盒子 4、弹性盒子 5、盒子内部分区 方法一&#xff1a;使用 float 普通盒子实现横向布局 方法二&#xff1a;使用 display: inline-block 内联块级元素实现横向布局 方法三&#xff1a;使用弹性盒子 flexbox&#xff0…

Go Map 源码分析(一)

Go语言中的map是通过哈希表实现的&#xff0c;其底层结构和实现机制如下&#xff1a; 一、hash 结构 hmap结构体&#xff1a;是map的头部结构&#xff0c;主要字段及含义如下&#xff1a; count&#xff1a;表示当前哈希表中的元素数量&#xff0c;与len()函数相对应。flags…

【大数据2025】Yarn 总结

分布式资源管理系统讲解总结 一、引言 围绕分布式资源管理系统展开&#xff0c;重点涵盖 Yarn 的简介、原理、资源调度策略以及运维和管理&#xff0c;旨在让学员全面掌握相关知识。Yet Another Resource Negotiator 二、Yarn 诞生背景 在 Hadoop 1.X 中仅有 HDFS 和 MapRe…

【AI日记】25.01.19

【AI论文解读】【AI知识点】【AI小项目】【AI战略思考】【AI日记】【读书与思考】 AI kaggle 比赛&#xff1a;Forecasting Sticker Sales 读书 书名&#xff1a;自由宪章阅读原因&#xff1a;作者哈耶克&#xff0c;诺贝尔经济学奖得主&#xff0c;之前读过他的 《通往奴役…

5.最长回文子串--力扣

给你一个字符串 s&#xff0c;找到 s 中最长的 回文子串。 示例 1&#xff1a; 输入&#xff1a;s “babad” 输出&#xff1a;“bab” 解释&#xff1a;“aba” 同样是符合题意的答案。 示例 2&#xff1a; 输入&#xff1a;s “cbbd” 输出&#xff1a;“bb” 原题如上&…

GCPAAS/DashBoard:完全免费的仪表盘设计,基于Vue+ElementUI+G2Plot+Echarts,开源代码,简单易用!还在等什么呢

嗨&#xff0c;大家好&#xff0c;我是小华同学&#xff0c;关注我们获得“最新、最全、最优质”开源项目和高效工作学习方法 GCPAAS/DashBoard&#xff0c;一款基于SpringBoot、MyBatisPlus、ElementUI、G2Plot、Echarts等技术栈的仪表盘设计器&#xff0c;具备仪表盘目录管理…

Linux——线程条件变量(同步)

Linux——多线程的控制-CSDN博客 文章目录 目录 文章目录 前言 一、条件变量是什么&#xff1f; 1、死锁的必要条件 1. 互斥条件&#xff08;Mutual Exclusion&#xff09; 2. 请求和保持条件&#xff08;Hold and Wait&#xff09; 3. 不可剥夺条件&#xff08;No Preemption&…

“AI 辅助决策系统:决策路上的智慧领航员

在当今瞬息万变的时代&#xff0c;无论是企业的运营管理&#xff0c;还是个人在生活中的重大抉择&#xff0c;都需要精准、高效的决策。然而&#xff0c;信息的繁杂和未来的不确定性&#xff0c;常常让决策变得困难重重。这时&#xff0c;AI 辅助决策系统宛如一位智慧的领航员&…

某讯一面,感觉问Redis的难度不是很大

前不久&#xff0c;有位朋友去某讯面试&#xff0c;他说被问到了很多关于 Redis 的问题&#xff0c;比如为什么用 Redis 作为 MySQL 的缓存&#xff1f;Redis 中大量 key 集中过期怎么办&#xff1f;如何保证缓存和数据库数据的一致性&#xff1f;我将它们整理出来&#xff0c;…

DDD - 微服务落地的技术实践

文章目录 Pre概述如何发挥微服务的优势怎样提供微服务接口原则微服务的拆分与防腐层的设计 去中心化的数据管理数据关联查询的难题Case 1Case 2Case 3 总结 Pre DDD - 软件退化原因及案例分析 DDD - 如何运用 DDD 进行软件设计 DDD - 如何运用 DDD 进行数据库设计 DDD - 服…

闪豆多平台视频批量下载器

1. 视频链接获取与解析 首先&#xff0c;在哔哩哔哩网页中随意点击一个视频&#xff0c;比如你最近迷上了一个UP主的美食制作视频&#xff0c;想要下载下来慢慢学。点击视频后&#xff0c;复制视频页面的链接。复制完成后&#xff0c;不要急着关闭浏览器&#xff0c;因为接下来…

【STM32-学习笔记-14-】FLASH闪存

文章目录 FALSH闪存一、FLASH简介二、FLASH基本结构三、FLASH解锁四、使用指针访问存储器五、FLASH擦除以及编程流程Ⅰ、程序存储器全擦除1. 读取FLASH_CR的LOCK位2. 检查LOCK位是否为13. 设置FLASH_CR的MER 1和STRT 1&#xff08;如果LOCK位0&#xff09;4. 检查FLASH_SR的B…

微信消息群发(定时群发)-UI自动化产品(基于.Net平台+C#)

整理 | 小耕家的喵大仙 出品 | CSDN&#xff08;ID&#xff1a;lichao19897314&#xff09; 关联源码及工具下载https://download.csdn.net/download/lichao19897314/90096681https://download.csdn.net/download/lichao19897314/90096681https://download.csdn.net/download/…

FPGA产业全景扫描

随着芯片种类日益丰富、功能日益强大&#xff0c;人们不禁好奇&#xff1a;一块FPGA是如何从最初的概念一步步呈现在我们面前的&#xff1f; FPGA设计、FPGA原型验证/仿真、FPGA板级调试和应用&#xff0c;是FPGA从概念到应用的必经之路。本文将围绕这几个核心环节&#xff0c…

SW - 钣金零件保存成DWG时,需要将折弯线去掉

文章目录 SW - 钣金零件保存成DWG时&#xff0c;需要将折弯线去掉概述笔记备注END SW - 钣金零件保存成DWG时&#xff0c;需要将折弯线去掉 概述 如果做需要弯折的切割件&#xff0c;最好做成钣金零件。 最近做了几个小钣金(将钣金展开&#xff0c;建立新草图&#xff0c;在2…

git系列之revert回滚

1. Git 使用cherry-pick“摘樱桃” step 1&#xff1a; 本地切到远程分支&#xff0c;对齐要对齐的base分支&#xff0c;举例子 localmap git pull git reset --hard localmap 对应的commit idstep 2&#xff1a; 执行cherry-pick命令 git cherry-pick abc123这样就会将远程…