apollo源码同时兼容mysql、postgresql、oracle解决思路
apollo源码采用的是jpa规范 Hibernate 进行持久化的ORM框架
解决思路:
思路一:使用jpa配置文件persistence.xml文件,根据使用的数据库动态加载实体类与数据库中实体类的映射关系,因此可以完全是用xml实现
思路二:由于xml配置的优先级高于注解配置,如果项目中既用到注解又用到xml,配置xml会覆盖注解的映射关系
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="persistenceUnit-mysql" transaction-type="RESOURCE_LOCAL" >
<mapping-file>META-INF/orm-mysql.xml</mapping-file>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect" />
</properties>
</persistence-unit>
<persistence-unit name="persistenceUnit-pg" transaction-type="RESOURCE_LOCAL" >
<mapping-file>META-INF/orm-pg.xml</mapping-file>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQL9Dialect" />
</properties>
</persistence-unit>
<persistence-unit name="persistenceUnit-oracle" transaction-type="RESOURCE_LOCAL" >
<mapping-file>META-INF/orm-oracle.xml</mapping-file>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.Oracle10gDialect" />
</properties>
</persistence-unit>
</persistence>
orm-mysql.xml
<entity-mappings version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm
http://xmlns.jcp.org/xml/ns/persistence/orm_2_1.xsd">
<entity class="com.ctrip.framework.apollo.common.entity.AppNamespace" name="AppNamespace"/>
<entity class="com.ctrip.framework.apollo.common.entity.App" name="App"/>
<entity class="com.ctrip.framework.apollo.biz.entity.Audit" name="Audit"/>
<entity class="com.ctrip.framework.apollo.biz.entity.Cluster" name="Cluster"/>
<entity class="com.ctrip.framework.apollo.biz.entity.Commit" name="Commit">
<attributes>
<!-- mysql longtext类型使用lob标注-->
<basic name="changeSets">
<lob/>
</basic>
</attributes>
</entity>
<entity class="com.ctrip.framework.apollo.biz.entity.GrayReleaseRule" name="GrayReleaseRule"/>
<entity class="com.ctrip.framework.apollo.biz.entity.Instance" name="Instance"/>
<entity class="com.ctrip.framework.apollo.biz.entity.InstanceConfig" name="InstanceConfig"/>
<entity class="com.ctrip.framework.apollo.biz.entity.Item" name="Item">
<attributes>
<!-- mysql longtext使用lob标注-->
<basic name="value">
<lob/>
</basic>
</attributes>
</entity>
<entity class="com.ctrip.framework.apollo.biz.entity.Namespace" name="Namespace"/>
<entity class="com.ctrip.framework.apollo.biz.entity.NamespaceLock" name="NamespaceLock"/>
<entity class="com.ctrip.framework.apollo.biz.entity.Privilege" name="Privilege"/>
<entity class="com.ctrip.framework.apollo.biz.entity.ReleaseHistory" name="ReleaseHistory"/>
<entity class="com.ctrip.framework.apollo.biz.entity.Release" name="Release">
<attributes>
<!-- mysql longtext使用lob标注-->
<basic name="configurations">
<lob/>
</basic>
</attributes>
</entity>
<entity class="com.ctrip.framework.apollo.biz.entity.ReleaseMessage" name="ReleaseMessage"/>
<entity class="com.ctrip.framework.apollo.biz.entity.ServerConfig" name="ServerConfig"/>
</entity-mappings>
orm-oracle.xml映射配置
<entity-mappings version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm
http://xmlns.jcp.org/xml/ns/persistence/orm_2_1.xsd">
<mapped-superclass class="com.ctrip.framework.apollo.common.entity.BaseEntity">
<attributes>
<id name="id">
<generated-value strategy="SEQUENCE" generator="sequence" />
</id>
</attributes>
</mapped-superclass>
<entity class="com.ctrip.framework.apollo.common.entity.AppNamespace" name="AppNamespace">
<sequence-generator name="sequence" allocation-size="1" sequence-name="appnamespace_id_seq"/>
<attributes>
<basic name="comment">
<column name ='"COMMENT"'></column>
</basic>
</attributes>
</entity>
<entity class="com.ctrip.framework.apollo.common.entity.App" name="App">
<sequence-generator name="sequence" allocation-size="1" sequence-name="app_id_seq"/>
</entity>
<entity class="com.ctrip.framework.apollo.biz.entity.Audit" name="Audit">
<table name='"AUDIT"'/>
<sequence-generator name="sequence" allocation-size="1" sequence-name="audit_id_seq"/>
<attributes>
<basic name="comment">
<column name ='"COMMENT"'></column>
</basic>
</attributes>
</entity>
<entity class="com.ctrip.framework.apollo.biz.entity.Cluster" name="Cluster">
<table name='"CLUSTER"'/>
<sequence-generator name="sequence" allocation-size="1" sequence-name="cluster_id_seq"/>
</entity>
<entity class="com.ctrip.framework.apollo.biz.entity.Commit" name="Commit">
<table name='"COMMIT"'/>
<sequence-generator name="sequence" allocation-size="1" sequence-name="commit_id_seq"/>
<attributes>
<basic name="comment">
<column name ='"COMMENT"'></column>
</basic>
</attributes>
</entity>
<entity class="com.ctrip.framework.apollo.biz.entity.GrayReleaseRule" name="GrayReleaseRule">
<sequence-generator name="sequence" allocation-size="1" sequence-name="grayreleaserule_id_seq"/>
</entity>
<entity class="com.ctrip.framework.apollo.biz.entity.Instance" name="Instance">
<attributes>
<id name="id">
<generated-value strategy="SEQUENCE" generator="instance_id_seq"/>
<sequence-generator name="instance_id_seq" sequence-name="instance_id_seq" allocation-size="1"/>
</id>
</attributes>
</entity>
<entity class="com.ctrip.framework.apollo.biz.entity.InstanceConfig" name="InstanceConfig">
<attributes>
<id name="id">
<generated-value strategy="SEQUENCE" generator="instanceconfig_id_seq"/>
<sequence-generator name="instanceconfig_id_seq" sequence-name="instanceconfig_id_seq" allocation-size="1"/>
</id>
</attributes>
</entity>
<entity class="com.ctrip.framework.apollo.biz.entity.Item" name="Item">
<sequence-generator name="sequence" allocation-size="1" sequence-name="item_id_seq"/>
<attributes>
<basic name="comment" >
<column name='"COMMENT"'/>
</basic>
</attributes>
</entity>
<entity class="com.ctrip.framework.apollo.biz.entity.Namespace" name="Namespace">
<sequence-generator name="sequence" allocation-size="1" sequence-name="namespace_id_seq"/>
</entity>
<entity class="com.ctrip.framework.apollo.biz.entity.NamespaceLock" name="NamespaceLock">
<sequence-generator name="sequence" allocation-size="1" sequence-name="namespacelock_id_seq"/>
</entity>
<entity class="com.ctrip.framework.apollo.biz.entity.Privilege" name="Privilege">
<sequence-generator name="sequence" allocation-size="1" sequence-name="privilege_id_seq"/>
</entity>
<entity class="com.ctrip.framework.apollo.biz.entity.Release" name="Release">
<sequence-generator name="sequence" allocation-size="1" sequence-name="release_id_seq"/>
<attributes>
<basic name="comment" >
<column name='"COMMENT"'/>
</basic>
</attributes>
</entity>
<entity class="com.ctrip.framework.apollo.biz.entity.ReleaseHistory" name="ReleaseHistory">
<sequence-generator name="sequence" allocation-size="1" sequence-name="releasehistory_id_seq"/>
</entity>
<entity class="com.ctrip.framework.apollo.biz.entity.ReleaseMessage" name="ReleaseMessage">
<attributes>
<id name="id">
<generated-value strategy="SEQUENCE" generator="releasemessage_id_seq"/>
<sequence-generator name="releasemessage_id_seq" sequence-name="releasemessage_id_seq" allocation-size="1"/>
</id>
</attributes>
</entity>
<entity class="com.ctrip.framework.apollo.biz.entity.ServerConfig" name="ServerConfig">
<sequence-generator name="sequence" allocation-size="1" sequence-name="serverconfig_id_seq"/>
<attributes>
<basic name="cluster" >
<column name='"CLUSTER"'/>
</basic>
<basic name="comment" >
<column name='"COMMENT"'/>
</basic>
</attributes>
</entity>
</entity-mappings>
orm-pg.xml映射配置
<entity-mappings version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm
http://xmlns.jcp.org/xml/ns/persistence/orm_2_1.xsd">
<mapped-superclass class="com.ctrip.framework.apollo.common.entity.BaseEntity">
<attributes>
<id name="id">
<generated-value strategy="SEQUENCE" generator="sequence" />
</id>
</attributes>
</mapped-superclass>
<entity class="com.ctrip.framework.apollo.common.entity.AppNamespace" name="AppNamespace">
<sequence-generator name="sequence" allocation-size="1" sequence-name="appnamespace_id_seq"/>
</entity>
<entity class="com.ctrip.framework.apollo.common.entity.App" name="App">
<sequence-generator name="sequence" allocation-size="1" sequence-name="app_id_seq"/>
</entity>
<entity class="com.ctrip.framework.apollo.biz.entity.Audit" name="Audit">
<sequence-generator name="sequence" allocation-size="1" sequence-name="audit_id_seq"/>
</entity>
<entity class="com.ctrip.framework.apollo.biz.entity.Cluster" name="Cluster">
<sequence-generator name="sequence" allocation-size="1" sequence-name="cluster_id_seq"/>
</entity>
<entity class="com.ctrip.framework.apollo.biz.entity.Commit" name="Commit">
<sequence-generator name="sequence" allocation-size="1" sequence-name="commit_id_seq"/>
</entity>
<entity class="com.ctrip.framework.apollo.biz.entity.GrayReleaseRule" name="GrayReleaseRule">
<sequence-generator name="sequence" allocation-size="1" sequence-name="grayreleaserule_id_seq"/>
</entity>
<entity class="com.ctrip.framework.apollo.biz.entity.Instance" name="Instance">
<attributes>
<id name="id">
<generated-value strategy="SEQUENCE" generator="instance_id_seq"/>
<sequence-generator name="instance_id_seq" sequence-name="instance_id_seq" allocation-size="1"/>
</id>
</attributes>
</entity>
<entity class="com.ctrip.framework.apollo.biz.entity.InstanceConfig" name="InstanceConfig">
<attributes>
<id name="id">
<generated-value strategy="SEQUENCE" generator="instanceconfig_id_seq"/>
<sequence-generator name="instanceconfig_id_seq" sequence-name="instanceconfig_id_seq" allocation-size="1"/>
</id>
</attributes>
</entity>
<entity class="com.ctrip.framework.apollo.biz.entity.Item" name="Item">
<sequence-generator name="sequence" allocation-size="1" sequence-name="item_id_seq"/>
</entity>
<entity class="com.ctrip.framework.apollo.biz.entity.Namespace" name="Namespace">
<sequence-generator name="sequence" allocation-size="1" sequence-name="namespace_id_seq"/>
</entity>
<entity class="com.ctrip.framework.apollo.biz.entity.NamespaceLock" name="NamespaceLock">
<sequence-generator name="sequence" allocation-size="1" sequence-name="namespacelock_id_seq"/>
</entity>
<entity class="com.ctrip.framework.apollo.biz.entity.Privilege" name="Privilege">
<sequence-generator name="sequence" allocation-size="1" sequence-name="privilege_id_seq"/>
</entity>
<entity class="com.ctrip.framework.apollo.biz.entity.Release" name="Release">
<sequence-generator name="sequence" allocation-size="1" sequence-name="release_id_seq"/>
</entity>
<entity class="com.ctrip.framework.apollo.biz.entity.ReleaseHistory" name="ReleaseHistory">
<sequence-generator name="sequence" allocation-size="1" sequence-name="releasehistory_id_seq"/>
</entity>
<entity class="com.ctrip.framework.apollo.biz.entity.ReleaseMessage" name="ReleaseMessage">
<attributes>
<id name="id">
<generated-value strategy="SEQUENCE" generator="releasemessage_id_seq"/>
<sequence-generator name="releasemessage_id_seq" sequence-name="releasemessage_id_seq" allocation-size="1"/>
</id>
</attributes>
</entity>
<entity class="com.ctrip.framework.apollo.biz.entity.ServerConfig" name="ServerConfig">
<sequence-generator name="sequence" allocation-size="1" sequence-name="serverconfig_id_seq"/>
</entity>
</entity-mappings>
动态加载配置类
package com.ctrip.framework.apollo.adminservice.configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.Map;
@Configuration
@EnableJpaRepositories(
entityManagerFactoryRef = "entityManagerFactory",
transactionManagerRef = "transactionManager",
basePackages = {"com.ctrip.framework.apollo.biz.repository"})
public class JpaConfig {
@Resource
private JpaProperties jpaProperties;
@Resource
private DataSourceProperties dataSourceProperties;
@Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setShowSql(jpaProperties.isShowSql());
hibernateJpaVendorAdapter.setGenerateDdl(jpaProperties.isGenerateDdl());
hibernateJpaVendorAdapter.setDatabase(jpaProperties.getDatabase());
return hibernateJpaVendorAdapter;
}
/**
* 数据源
*/
@Bean(name = "dataSource")
public DataSource dataSource() {
//apollo默认使用的是org.apache.tomcat.jdbc.pool.DataSource
org.apache.tomcat.jdbc.pool.DataSource dataSource = new org.apache.tomcat.jdbc.pool.DataSource();
dataSource.setUrl(dataSourceProperties.getUrl());
dataSource.setUsername(dataSourceProperties.getUsername());
dataSource.setPassword(dataSourceProperties.getPassword());
dataSource.setDriverClassName(dataSourceProperties.getDriverClassName());
dataSource.setValidationQuery(dataSourceProperties.getValidationQuery());
dataSource.setValidationInterval(dataSourceProperties.getValidationInterval());
dataSource.setTestWhileIdle(dataSourceProperties.isTestWhileIdle());
dataSource.setTestOnBorrow(dataSourceProperties.isTestOnBorrow());
if (dataSourceProperties.getUrl().contains("jdbc:mysql")) {
dataSource.setInitSQL(dataSourceProperties.getInitSql());
}
return dataSource;
}
/**
* 创建 LocalContainerEntityManagerFactoryBean
*/
@Bean(name = "entityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManager = new LocalContainerEntityManagerFactoryBean();
entityManager.setDataSource(this.dataSource());
entityManager.setJpaVendorAdapter(jpaVendorAdapter());
entityManager.setPackagesToScan("com.ctrip.framework.apollo.biz.entity","com.ctrip.framework.apollo.common.entity");
Map<String, String> jpaProperties = this.jpaProperties.getProperties();
entityManager.setJpaPropertyMap(jpaProperties);
//PersistenceUnit默认使用mysql的persistenceUnit-mysql, pg方式时切换到persistenceUnit-pg
//PersistenceUnit在persistence.xml中定义
entityManager.setPersistenceUnitName("persistenceUnit-mysql");
if (dataSourceProperties.getUrl().contains("jdbc:postgresql")) {
entityManager.setPersistenceUnitName("persistenceUnit-pg");
}
if(dataSourceProperties.getUrl().contains("jdbc:oracle")){
entityManager.setPersistenceUnitName("persistenceUnit-oracle");
}
return entityManager;
}
}
-----set 、get省略
@Component
@ConfigurationProperties("spring.datasource")
public class DataSourceProperties {
private String driverClassName;
private String url = "";
private String username;
private String password;
private int maxPoolSize;
private int minPoolSize;
private String uniqueResourceName;
private String validationQuery;
private int validationInterval;
private String initSql;
private boolean testWhileIdle;
private boolean testOnBorrow;
}
package com.ctrip.framework.apollo.adminservice.configuration;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDatabaseConnection;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.orm.jpa.vendor.Database;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* External configuration properties for a JPA EntityManagerFactory created by Spring.
*/
@Component
@ConfigurationProperties(prefix = "spring.jpa")
public class JpaProperties {
private static final Log logger = LogFactory.getLog(JpaProperties.class);
/**
* Additional native properties to set on the JPA provider.
*/
private Map<String, String> properties = new HashMap<String, String>();
/**
* Name of the target database to operate on, auto-detected by default. Can be
* alternatively set using the "Database" enum.<br>
* for example:org.hibernate.dialect.MySQLDialect
*/
private String databasePlatform;
/**
* Target database to operate on, auto-detected by default. Can be alternatively set
* using the "databasePlatform" property.
*/
private Database database = Database.DEFAULT;
/**
* Initialize the schema on startup.
*/
private boolean generateDdl = false;
/**
* Enable logging of SQL statements.
*/
private boolean showSql = false;
private Hibernate hibernate = new Hibernate();
public Map<String, String> getProperties() {
return this.properties;
}
public void setProperties(Map<String, String> properties) {
this.properties = properties;
}
public String getDatabasePlatform() {
return this.databasePlatform;
}
public void setDatabasePlatform(String databasePlatform) {
this.databasePlatform = databasePlatform;
}
public Database getDatabase() {
return this.database;
}
public void setDatabase(Database database) {
this.database = database;
}
public boolean isGenerateDdl() {
return this.generateDdl;
}
public void setGenerateDdl(boolean generateDdl) {
this.generateDdl = generateDdl;
}
public boolean isShowSql() {
return this.showSql;
}
public void setShowSql(boolean showSql) {
this.showSql = showSql;
}
public Hibernate getHibernate() {
return this.hibernate;
}
public void setHibernate(Hibernate hibernate) {
this.hibernate = hibernate;
}
/**
* Get configuration properties for the initialization of the main Hibernate
* EntityManagerFactory.
* @param dataSource the DataSource in case it is needed to determine the properties
* @return some Hibernate properties for configuration
*/
public Map<String, String> getHibernateProperties(DataSource dataSource) {
return this.hibernate.getAdditionalProperties(this.properties, dataSource);
}
public static class Hibernate {
private static final String DEFAULT_NAMING_STRATEGY = "org.springframework.boot.orm.jpa.hibernate.SpringNamingStrategy";
/**
* Naming strategy fully qualified name.
*/
private Class<?> namingStrategy;
/**
* DDL mode ("none", "validate", "update", "create", "create-drop"). This is
* actually a shortcut for the "hibernate.hbm2ddl.auto" property. Default to
* "create-drop" when using an embedded database, "none" otherwise.
*/
private String ddlAuto;
private boolean globallyQuotedIdentifiers;
public boolean isGloballyQuotedIdentifiers() {
return globallyQuotedIdentifiers;
}
public void setGloballyQuotedIdentifiers(boolean globallyQuotedIdentifiers) {
this.globallyQuotedIdentifiers = globallyQuotedIdentifiers;
}
public Class<?> getNamingStrategy() {
return this.namingStrategy;
}
public void setNamingStrategy(Class<?> namingStrategy) {
this.namingStrategy = namingStrategy;
}
@Deprecated
public void setNamingstrategy(Class<?> namingStrategy) {
logger.warn("The property spring.jpa.namingstrategy has been renamed, "
+ "please update your configuration to use namingStrategy or naming-strategy or naming_strategy");
this.setNamingStrategy(namingStrategy);
}
public String getDdlAuto() {
return this.ddlAuto;
}
public void setDdlAuto(String ddlAuto) {
this.ddlAuto = ddlAuto;
}
private Map<String, String> getAdditionalProperties(Map<String, String> existing,
DataSource dataSource) {
Map<String, String> result = new HashMap<String, String>(existing);
if (!isAlreadyProvided(existing, "ejb.naming_strategy")
&& this.namingStrategy != null) {
result.put("hibernate.ejb.naming_strategy", this.namingStrategy.getName());
}
else if (this.namingStrategy == null) {
result.put("hibernate.ejb.naming_strategy", DEFAULT_NAMING_STRATEGY);
}
String ddlAuto = getOrDeduceDdlAuto(existing, dataSource);
if (StringUtils.hasText(ddlAuto) && !"none".equals(ddlAuto)) {
result.put("hibernate.hbm2ddl.auto", ddlAuto);
}
else {
result.remove("hibernate.hbm2ddl.auto");
}
return result;
}
private String getOrDeduceDdlAuto(Map<String, String> existing,
DataSource dataSource) {
String ddlAuto = (this.ddlAuto != null ? this.ddlAuto
: getDefaultDdlAuto(dataSource));
if (!isAlreadyProvided(existing, "hbm2ddl.auto") && !"none".equals(ddlAuto)) {
return ddlAuto;
}
if (isAlreadyProvided(existing, "hbm2ddl.auto")) {
return existing.get("hibernate.hbm2ddl.auto");
}
return "none";
}
private String getDefaultDdlAuto(DataSource dataSource) {
if (EmbeddedDatabaseConnection.isEmbedded(dataSource)) {
return "create-drop";
}
return "none";
}
private boolean isAlreadyProvided(Map<String, String> existing, String key) {
return existing.containsKey("hibernate." + key);
}
}
}
数据库坐标依赖
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.4</version>
</dependency>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc14</artifactId>
<version>10.2.0.4.0</version>
</dependency>
本地实现的环境:
<java.version>1.8</java.version>
<tomcat.version>8.5.23</tomcat.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-boot.version>1.3.8.RELEASE</spring-boot.version>
<spring-cloud.version>1.2.3.RELEASE</spring-cloud.version>
<!-- Plugins Version -->
<maven-compiler-plugin.version>3.6.0</maven-compiler-plugin.version>