{"id":13694880,"url":"https://github.com/chengdedeng/perseus","last_synced_at":"2025-05-03T04:31:08.140Z","repository":{"id":85035465,"uuid":"95118617","full_name":"chengdedeng/perseus","owner":"chengdedeng","description":":zap:database read and write separation of java","archived":false,"fork":false,"pushed_at":"2018-03-01T06:36:34.000Z","size":81,"stargazers_count":119,"open_issues_count":0,"forks_count":42,"subscribers_count":20,"default_branch":"master","last_synced_at":"2025-01-07T19:49:24.773Z","etag":null,"topics":["database-separation","datasource","java-mybatis-spring","read-write-separation","transaction"],"latest_commit_sha":null,"homepage":"https://github.com/chengdedeng/perseus","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/chengdedeng.png","metadata":{"files":{"readme":"README-ZH.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2017-06-22T13:29:41.000Z","updated_at":"2025-01-05T15:29:14.000Z","dependencies_parsed_at":null,"dependency_job_id":"8c3f1256-97cf-4904-bcb4-4e0de3822761","html_url":"https://github.com/chengdedeng/perseus","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chengdedeng%2Fperseus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chengdedeng%2Fperseus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chengdedeng%2Fperseus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chengdedeng%2Fperseus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chengdedeng","download_url":"https://codeload.github.com/chengdedeng/perseus/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252144552,"owners_count":21701430,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["database-separation","datasource","java-mybatis-spring","read-write-separation","transaction"],"created_at":"2024-08-02T17:01:46.624Z","updated_at":"2025-05-03T04:31:05.047Z","avatar_url":"https://github.com/chengdedeng.png","language":"Java","readme":"[![Join the chat at https://gitter.im/chengdedeng/perseus](https://badges.gitter.im/chengdedeng/perseus.svg)](https://gitter.im/chengdedeng/perseus?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n## [English document](/README-ZH.md)\n\n### 项目介绍\n数据库读写分离是再基础不过的需求了,读写分离通常有三种方案实现:\n1. 多数据源,通过代码硬编码实现.\n2. 修改ORM框架实现.\n3. 实现数据库协议来实现.\n\n方案一最简单,但是开发人员工作量最大,并且容易犯错;虽然方案三开发人员来说是透明的且不限制编程语言,但是开发难度最大且数据库的支持范围\n较窄.本项目基于方案二,选择了Java中最流行的Mybatis和Spring来实现,所以只适用于基于Mybatis+Spring实现的Java项目.\n\n\n### 功能\n1. 事务一律到主库,不区分transaction是否是readonly.由于readonly并不真正的启动事务,只是激活transaction synchronization,因此并不会被[DynamicDataSourceTransactionManager](/src/main/java/info/yangguo/perseus/DynamicDataSourceTransactionManager.java)截获,\n所以就被default(master database)设置命中,并且一个read only transaction中的所有语句的执行都会复用同一个JDBCConnection(SqlSession).\n2. select到读库,insert/update/delete到主库.\n3. 支持select强制路由到主库(尽量避免,通过业务逻辑优化来绕过).\n4. 支持mybatis-spring中的batch操作.\n\n\n### 稳定度\n该项目在笔者公司,上百个项目中广泛应用,已经很成熟,测试代码中有详细的配置和测试代码.\n\n\n### 核心配置\n\n#### 常规配置\n\n```\n    \u003cbean id=\"dataSource\" class=\"info.yangguo.perseus.DynamicDataSource\"\u003e\n        \u003cproperty name=\"master\" ref=\"master\"/\u003e\n        \u003cproperty name=\"slaves\"\u003e\n            \u003clist\u003e\n                \u003cref bean=\"slave1\"/\u003e\n                \u003cref bean=\"slave2\"/\u003e\n            \u003c/list\u003e\n        \u003c/property\u003e\n    \u003c/bean\u003e\n\n    \u003c!-- ibatis3 工厂类 --\u003e\n    \u003cbean id=\"sqlSessionFactory\" class=\"org.mybatis.spring.SqlSessionFactoryBean\"\u003e\n        \u003cproperty name=\"dataSource\" ref=\"dataSource\"/\u003e\n        \u003cproperty name=\"typeAliasesPackage\" value=\"info.yangguo.perseus.test.domain\"/\u003e\n        \u003cproperty name=\"configLocation\" value=\"classpath:sqlMapConfig.xml\"/\u003e\n        \u003cproperty name=\"mapperLocations\" value=\"classpath:info/yangguo/perseus/test/dao/*.xml\"/\u003e\n    \u003c/bean\u003e\n\n    \u003cbean class=\"info.yangguo.perseus.MapperScannerConfigurer\"\u003e\n        \u003cproperty name=\"basePackage\" value=\"info.yangguo.perseus.test.dao\"/\u003e\n        \u003cproperty name=\"sqlSessionFactoryBeanName\" value=\"sqlSessionFactory\"/\u003e\n    \u003c/bean\u003e\n\n    \u003c!-- 定义单个jdbc数据源的事务管理器 --\u003e\n    \u003cbean id=\"transactionManager\"\n          class=\"info.yangguo.perseus.DynamicDataSourceTransactionManager\"\u003e\n        \u003cproperty name=\"dataSource\" ref=\"dataSource\"/\u003e\n    \u003c/bean\u003e\n    \u003c!-- 以 @Transactional 标注来定义事务  --\u003e\n    \u003ctx:annotation-driven transaction-manager=\"transactionManager\" proxy-target-class=\"true\"/\u003e\n```\n\n\n#### batch操作配置\n\n```\n    \u003cbean id=\"dataSource\" class=\"info.yangguo.perseus.DynamicDataSource\"\u003e\n        \u003cproperty name=\"master\" ref=\"master\"/\u003e\n        \u003cproperty name=\"slaves\"\u003e\n            \u003clist\u003e\n                \u003cref bean=\"slave1\"/\u003e\n                \u003cref bean=\"slave2\"/\u003e\n            \u003c/list\u003e\n        \u003c/property\u003e\n    \u003c/bean\u003e\n\n    \u003c!-- transaction manager, use JtaTransactionManager for global tx --\u003e\n    \u003cbean id=\"transactionManager\"\n          class=\"info.yangguo.perseus.DynamicDataSourceTransactionManager\"\u003e\n        \u003cproperty name=\"dataSource\" ref=\"dataSource\"/\u003e\n    \u003c/bean\u003e\n\n    \u003c!-- enable transaction demarcation with annotations --\u003e\n    \u003ctx:annotation-driven/\u003e\n\n    \u003c!-- simplest possible SqlSessionFactory configuration --\u003e\n    \u003cbean id=\"sqlSessionFactory\" class=\"org.mybatis.spring.SqlSessionFactoryBean\"\u003e\n        \u003cproperty name=\"dataSource\" ref=\"dataSource\"/\u003e\n        \u003cproperty name=\"mapperLocations\" value=\"classpath:EmployeeMapper.xml\"/\u003e\n    \u003c/bean\u003e\n    \u003c!-- item reader  --\u003e\n    \u003cbean id=\"pagingNoNestedItemReader\" class=\"info.yangguo.perseus.DynamicMyBatisPagingItemReader\"\u003e\n        \u003cproperty name=\"sqlSessionFactory\" ref=\"sqlSessionFactory\"/\u003e\n        \u003cproperty name=\"sqlSessionTemplate\" ref=\"dynamicSqlSession\"/\u003e\n        \u003cproperty name=\"queryId\" value=\"getEmployeeNoNestedPaging\"/\u003e\n        \u003cproperty name=\"pageSize\" value=\"5\"/\u003e\n    \u003c/bean\u003e\n\n    \u003cbean id=\"pagingNestedItemReader\" class=\"info.yangguo.perseus.DynamicMyBatisPagingItemReader\"\u003e\n        \u003cproperty name=\"sqlSessionFactory\" ref=\"sqlSessionFactory\"/\u003e\n        \u003cproperty name=\"sqlSessionTemplate\" ref=\"dynamicSqlSession\"/\u003e\n        \u003cproperty name=\"queryId\" value=\"getEmployeeNestedPaging\"/\u003e\n        \u003cproperty name=\"pageSize\" value=\"5\"/\u003e\n    \u003c/bean\u003e\n\n    \u003cbean id=\"cursorNoNestedItemReader\" class=\"info.yangguo.perseus.DynamicMyBatisCursorItemReader\"\u003e\n        \u003cproperty name=\"sqlSessionFactory\" ref=\"sqlSessionFactory\"/\u003e\n        \u003cproperty name=\"queryId\" value=\"getEmployeeNoNestedCursor\"/\u003e\n    \u003c/bean\u003e\n\n    \u003cbean id=\"cursorNestedItemReader\" class=\"info.yangguo.perseus.DynamicMyBatisCursorItemReader\"\u003e\n        \u003cproperty name=\"sqlSessionFactory\" ref=\"sqlSessionFactory\"/\u003e\n        \u003cproperty name=\"queryId\" value=\"getEmployeeNestedCursor\"/\u003e\n    \u003c/bean\u003e\n\n    \u003cbean id=\"writer\" class=\"org.mybatis.spring.batch.MyBatisBatchItemWriter\"\u003e\n        \u003cproperty name=\"sqlSessionFactory\" ref=\"sqlSessionFactory\"/\u003e\n        \u003cproperty name=\"statementId\" value=\"updateEmployee\"/\u003e\n    \u003c/bean\u003e\n\n    \u003cbean id=\"sqlSession\" class=\"org.mybatis.spring.SqlSessionTemplate\"\u003e\n        \u003cconstructor-arg index=\"0\" ref=\"sqlSessionFactory\"/\u003e\n        \u003cconstructor-arg index=\"1\" value=\"BATCH\"/\u003e\n    \u003c/bean\u003e\n    \u003cbean id=\"dynamicSqlSession\" class=\"info.yangguo.perseus.DynamicSqlSessionTemplate\"\u003e\n        \u003cconstructor-arg index=\"0\" ref=\"sqlSession\"/\u003e\n    \u003c/bean\u003e\n```\n","funding_links":[],"categories":["Java"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchengdedeng%2Fperseus","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchengdedeng%2Fperseus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchengdedeng%2Fperseus/lists"}