我想将我的测试数据库(mysql)中的所有数据复制到生产数据库(hsqldb) 我使用了hibernate,并让它为这些数据库中的表创建了表。所以结构/模式是一样的。在不同数据库之间复制数据(都支持jdbc)
使用mysql转储我可以在两个mysql数据库之间复制数据。但在我的情况下,数据库是不同的,我听说从mysqldump生成的sql不会与hsqldb一起工作。既然这两种数据库类型都是由jdbc/hibernate支持的,那么在它们之间是否有任何方法/方法/ java库来复制数据?
我想将我的测试数据库(mysql)中的所有数据复制到生产数据库(hsqldb) 我使用了hibernate,并让它为这些数据库中的表创建了表。所以结构/模式是一样的。在不同数据库之间复制数据(都支持jdbc)
使用mysql转储我可以在两个mysql数据库之间复制数据。但在我的情况下,数据库是不同的,我听说从mysqldump生成的sql不会与hsqldb一起工作。既然这两种数据库类型都是由jdbc/hibernate支持的,那么在它们之间是否有任何方法/方法/ java库来复制数据?
您可以使用相同的映射创建两个会话工厂,一个用于MySql,另一个用于HSQLDB。之后,您可以简单地从MySQL获取数据并将其存储到HSQLDB。
当然,如果图式相同,可以非常简单的方式进行。而且,由于您使用相同的Hibernate映射创建了两个数据库,因此它们应该在Entity
意义上相等。
您只需要两个Hibernate持久性单元(数据源)。如果两者都配置正确,并且你有特殊的实例,那么就直接进入Hibernate Session
级别 - 据我所知JPA不支持这种方式(如果我错了,请纠正我) - 然后复制源代码实体到您的目标数据库。
因为我喜欢使用Spring,所以我将使用Spring Boot作为下面的示例。除配置外,复制步骤将与任何Hibernate应用程序一样实施。
我也使用两个PostgreSQL数据库而不是HSQLB来保持简单。只要扩展配置部分,如果你的配置不同,我的持久性单元之间的唯一区别就是数据源url。
所以首先我们需要一个实体来测试复制:
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class StorageEntry {
@Id
@GeneratedValue
private Long id;
private String someValue;
// imagine getters and setter here
}
这是(在YAML版)两个数据源的配置(见叫targetDatabaseUrl
第二个数据源URL)的所有其它部件该配置将同时用于持久单元:
spring:
datasource:
url: jdbc:postgresql://localhost/postgres
targetDatabaseUrl: jdbc:postgresql://localhost/postgres2
username: <username>
password: <password>
driver-class-name: org.postgresql.Driver
jpa:
database-platform: org.hibernate.dialect.PostgreSQLDialect
hibernate:
ddl-auto: create-drop
接下来的部分是用于数据源的配置类:
import java.util.Properties;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
@Configuration
public class PersistenceConfig {
@Autowired
private JpaVendorAdapter jpaVendorAdapter;
@Value("${spring.datasource.url}")
private String databaseUrl;
@Value("${spring.datasource.targetDatabaseUrl}")
private String targetDatabaseUrl;
@Value("${spring.datasource.username}")
private String username;
@Value("${spring.datasource.password}")
private String password;
@Value("${spring.datasource.driver-class-name}")
private String driverClassName;
@Value("${spring.jpa.database-platform}")
private String dialect;
@Value("${spring.jpa.hibernate.ddl-auto}")
private String ddlAuto;
@Bean
public EntityManager sourceEntityManager() {
return sourceEntityManagerFactory().createEntityManager();
}
@Bean
public EntityManager targetEntityManager() {
return targetEntityManagerFactory().createEntityManager();
}
@Bean
public EntityManagerFactory sourceEntityManagerFactory() {
return createEntityManagerFactory("source", databaseUrl);
}
@Bean
public EntityManagerFactory targetEntityManagerFactory() {
return createEntityManagerFactory("target", targetDatabaseUrl);
}
@Bean
public PlatformTransactionManager sourceTransactionManager() {
return new JpaTransactionManager(sourceEntityManagerFactory());
}
@Bean
public PlatformTransactionManager targetTransactionManager() {
return new JpaTransactionManager(targetEntityManagerFactory());
}
private EntityManagerFactory createEntityManagerFactory(final String persistenceUnitName,
final String databaseUrl) {
final LocalContainerEntityManagerFactoryBean entityManagerFactory = new LocalContainerEntityManagerFactoryBean();
final DriverManagerDataSource dataSource = new DriverManagerDataSource(databaseUrl, username, password);
dataSource.setDriverClassName(driverClassName);
entityManagerFactory.setDataSource(dataSource);
entityManagerFactory.setJpaVendorAdapter(jpaVendorAdapter);
entityManagerFactory.setPackagesToScan("com.example.model");
entityManagerFactory.setPersistenceUnitName(persistenceUnitName);
final Properties properties = new Properties();
properties.setProperty("hibernate.dialect", dialect);
properties.setProperty("hibernate.hbm2ddl.auto", ddlAuto);
entityManagerFactory.setJpaProperties(properties);
entityManagerFactory.afterPropertiesSet();
return entityManagerFactory.getObject();
}
}
现在您可以使用不同的实体管理器来简单地将数据从一个数据源读取和写入另一个数据源。要说明的是这里是一个小的测试案例:
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.hibernate.ReplicationMode;
import org.hibernate.Session;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;
import com.example.model.StorageEntry;
@SpringBootTest
@RunWith(SpringRunner.class)
@Transactional(transactionManager = "targetTransactionManager")
public class ReplicationTests {
@PersistenceContext(unitName = "source")
private EntityManager sourceEntityManager;
@PersistenceContext(unitName = "target")
private EntityManager targetEntityManager;
@Test
public void copyEntityBetweenPersistenceUnits() {
final StorageEntry entityToCopy = new StorageEntry();
entityToCopy.setSomeValue("copyMe!");
sourceEntityManager.persist(entityToCopy);
final Long id = entityToCopy.getId();
final StorageEntry sourceEntity = sourceEntityManager.find(StorageEntry.class, id);
assertThat("Entity should exist in default schema!", sourceEntity, notNullValue());
StorageEntry targetEntity = targetEntityManager.find(StorageEntry.class, id);
assertThat("Target schema should not contain the entity, yet!", targetEntity, nullValue());
final Session hibernateSession = targetEntityManager.unwrap(Session.class);
hibernateSession.replicate(sourceEntity, ReplicationMode.OVERWRITE);
targetEntityManager.flush();
targetEntityManager.clear();
targetEntity = targetEntityManager.find(StorageEntry.class, id);
assertThat("Entity should be copied now!", targetEntity, notNullValue());
}
}
最后,选择哪个适合您需求的possible replication modes之一。
就是这样。你甚至可以使用一个事务,只需决定两个持久化单元中的一个,并利用它的事务管理器,就像@Transactional(transactionManager = "targetTransactionManager")
的测试一样。
当然,这个例子可以为1个实体工作,但对于真正的db模式来说这是不现实的。这种迁移方案更适合ETL的角色。 –
当然,真正的迁移应该在其他层次上完成,使用数据库迁移工具或比这里的示例更复杂的应用程序结构。问题只是关于如何在jdbc级别复制数据。这不是生产就绪代码。:) –
我试过你的例子,但我无法理解在Test类中,源或目标EntityManager的数据都不能保存到Postgresql数据库中?尽管测试可能会成功。 –