从单个整体springboot应用程序对两个独立数据库中的表执行写入操作

时间:2019-07-29 19:13:59

标签: java mysql hibernate spring-boot database-migration

我在工作时提供整体服务。它连接到单个mysql数据库,例如X。 X有很多表。 X现在已成为扩大规模的瓶颈,因此我正在努力将非关键表从X移到另一个实例Y中。目前,我已将一些审计表从X移到Y。 行动计划如下: 1)DBA团队正在使用X的目标表启动一个新的数据库实例。不确定确切的细节。 2)在应用程序方面,我正在考虑创建将连接到新实例Y的多个休眠会话工厂。然后,我计划使用功能门标志来修改目标表的应用程序的读/写行为。 2.1)写入功能标志的值将为0(写入旧数据库),1(写入旧数据库和新数据库),2(写入新数据库)。 2.2)读取功能标志的值将为0(从旧数据库读取),1(从新数据库读取)。

在部署之日, 1)我们计划停工 2)DBA团队将为新实例做好准备 3)我将应用程序的写入功能标志设置为模式1,而读取功能标志仍然设置为0。如果安装程序可以正常运行几个星期,则将读取标志迁移到1并将写入标志迁移到2。

请帮助我解决计划中的缺陷。

整体服务是旧服务,是带有spring orm的springboot 1.4.0。 MySQL版本是5.7 谈到我的主要问题,即关于启用设置(例如写入两个单独的数据库中的表)的问题。我已跟踪链接Hibernate configuring multiple datasources and multiple session factories,应用程序已连接到多个数据库。我已经复制了目标表的实体和路径。在服务方法中,我添加了基于功能标记编写的代码。但是,当我在写入模式1下测试代码时,数据将被持久化到X,但数据不会被持久化到Y。我用format_sql和show_sql true调试了很多,但是我无法找出原因。

通过续集pro访问db X和Y,我已经验证了数据已保存在X的目标表中,而不保存在Y的目标表中。尽管当我通过查询在Y的目标表中插入一行时,自动生成的主ID字段的值增加了。

@Entity
@Data
@Table(name = "time_audit")
public class TimeAudit {
    @Id
    @GeneratedValue(strategy = IDENTITY)
    @Column(name="id") private long id;
    @Column(name="order_id") private String orderId;
    @Column(name = "status") private String status;
    @Column(name = "duration") private Double duration;
    @Column(name = "calculated_for") private String calculatedFor;
}

@Entity
@Data
@Table(name = "time_audit_v2")
public class TimeAuditV2 {

    @Id
    @GeneratedValue(strategy = IDENTITY)
    @Column(name = "id")
    private long id;

    @Column(name = "order_id")
    private String orderId;

    @Column(name = "status")
    private String status;

    @Column(name = "duration")
    private Double duration;

    @Column(name = "calculated_for")
    private String calculatedFor;
}

@Repository
public class TimeAuditDaoImpl implements TimeAuditDao {
    @Autowired
    @Qualifier("sessionFactory") private SessionFactory sessionFactory;

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void saveTimeAudit(Double time, String orderId, String status, String calculatedFor){
        Session session = sessionFactory.getCurrentSession();
        TimeAudit timeAudit = new TimeAudit();
        timeAudit.setDuration(time);
        timeAudit.setOrderId(orderId);
        timeAudit.setStatus(status);
        timeAudit.setCalculatedFor(calculatedFor);
        session.save(timeAudit);
    }
}

@Repository
public class TimeAuditDaoImplV2 implements TimeAuditDaoV2 {

    @Autowired
    @Qualifier("taskSeqAuditSessionFactory")
    private SessionFactory taskSeqAuditSessionFactory;

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void saveTimeAudit(Double time, String orderId, String status, String calculatedFor) {
        Session session = taskSeqAuditSessionFactory.getCurrentSession();
        TimeAuditV2 timeAudit = new TimeAuditV2();
        timeAudit.setDuration(time);
        timeAudit.setOrderId(orderId);
        timeAudit.setStatus(status);
        timeAudit.setCalculatedFor(calculatedFor);
        session.save(timeAudit);
    }
}

@Service
public class TimeAuditServiceImpl implements TimeAuditService {

    @Autowired
    private DeliveryConfigDao deliveryConfigDao;

    @Autowired
    private TimeAuditDao timeAuditDao;

    @Autowired
    private TimeAuditDaoV2 timeAuditDaoV2;


    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void saveTimeAudit(Double time, String orderId, String status, String calculatedFor) {
        String taskSeqAuditSwitch = deliveryConfigDao.getConfig(GenericStringConstants.TASK_SEQ_AUDIT_WRITE_FEATURE_KEY, GenericStringConstants.TASK_SEQ_AUDIT_WRITE_DELIVERY_DB);
        if (taskSeqAuditSwitch.equalsIgnoreCase(GenericStringConstants.TASK_SEQ_AUDIT_WRITE_DELIVERY_DB)) {
            timeAuditDao.saveTimeAudit(time, orderId, status, calculatedFor);
        } else if (taskSeqAuditSwitch.equalsIgnoreCase(GenericStringConstants.TASK_SEQ_AUDIT_WRITE_BOTH_DB)) {
            timeAuditDao.saveTimeAudit(time, orderId, status, calculatedFor);
            timeAuditDaoV2.saveTimeAudit(time, orderId, status, calculatedFor);
        } else if (taskSeqAuditSwitch.equalsIgnoreCase(GenericStringConstants.TASK_SEQ_AUDIT_WRITE_TASK_SEQ_DB)) {
            timeAuditDaoV2.saveTimeAudit(time, orderId, status, calculatedFor);
        }
    }
}

[database.xml]
<bean id="slaveSessionFactory"
        class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">

        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">com.swiggy.delivery.deliveryboy.model.dao.CustomMySql5Dialect</prop>
                <prop key="hibernate.show_sql">${hibernate.slave.showSql}</prop>
                <prop key="hibernate.format_sql">${hibernate.slave.formatSql}</prop>
                <prop key="hibernate.connection.url">${hibernate.slave.connection.url}</prop>
                <prop key="hibernate.connection.driver_class">com.mysql.jdbc.ReplicationDriver</prop>
                <prop key="hibernate.connection.username">${hibernate.slave.connection.username}</prop>
                <prop key="hibernate.connection.password">${hibernate.slave.connection.password}</prop>
                <prop key="hibernate.c3p0.min_size">${hibernate.slave.c3p0.min_size}</prop>
                <prop key="hibernate.c3p0.max_size">${hibernate.slave.c3p0.max_size}</prop>
                <prop key="hibernate.c3p0.timeout">300</prop>
                <prop key="hibernate.c3p0.max_statements">0</prop>
                <prop key="hibernate.c3p0.idle_test_period">3000</prop>
                <prop key="hibernate.c3p0.max_idle_time_excess_connections">60</prop>
            </props>
        </property>

        <property name="packagesToScan"
                  value="com.swiggy.delivery.data.sql.entities, com.swiggy.delivery.referral.data.sql.entities, com.swiggy.delivery.sno.data.sql.entities, com.swiggy.delivery.apartmentSecurityPartners.data.sql.entities, com.swiggy.delivery.callStatus.data.sql.entities,
com.swiggy.delivery.loyaltyProgram.entities,com.swiggy.delivery.daily.entities,com.swiggy.delivery.service_line.entities,com.swiggy.delivery.hub.entities, com.swiggy.delivery.userCityMapping.entities,com.swiggy.delivery.userZoneMapping.entities,com.swiggy.delivery.userHubMapping.entities,com.swiggy.delivery.nudge.de.sql.entities,com.swiggy.delivery.bank.account.validation.entities"/>
    </bean>
    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" primary="true">

        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                <prop key="hibernate.show_sql">${hibernate.showSql}</prop>
                <prop key="hibernate.format_sql">${hibernate.formatSql}</prop>
                <prop key="hibernate.connection.url">${hibernate.connection.url}</prop>
                <prop key="hibernate.connection.driver_class">com.mysql.jdbc.Driver</prop>
                <prop key="hibernate.connection.username">${hibernate.connection.username}</prop>
                <prop key="hibernate.connection.password">${hibernate.connection.password}</prop>
                <prop key="hibernate.c3p0.min_size">${hibernate.c3p0.min_size}</prop>
                <prop key="hibernate.c3p0.max_size">${hibernate.c3p0.max_size}</prop>
                <prop key="hibernate.c3p0.timeout">300</prop>
                <prop key="hibernate.c3p0.max_statements">0</prop>
                <prop key="hibernate.c3p0.idle_test_period">3000</prop>
            </props>
        </property>
    </bean>

    <bean id="taskSeqAuditSessionFactory"
          class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" >

        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                <prop key="hibernate.show_sql">${hibernate.taskSeqAudit.showSql}</prop>
                <prop key="hibernate.format_sql">${hibernate.taskSeqAudit.formatSql}</prop>
                <prop key="hibernate.connection.url">${hibernate.taskSeqAudit.connection.url}</prop>
                <prop key="hibernate.connection.driver_class">com.mysql.jdbc.Driver</prop>
                <prop key="hibernate.connection.username">${hibernate.taskSeqAudit.connection.username}</prop>
                <prop key="hibernate.connection.password">${hibernate.taskSeqAudit.connection.password}</prop>
                <prop key="hibernate.c3p0.min_size">${hibernate.taskSeqAudit.c3p0.min_size}</prop>
                <prop key="hibernate.c3p0.max_size">${hibernate.taskSeqAudit.c3p0.max_size}</prop>
                <prop key="hibernate.c3p0.timeout">300</prop>
                <prop key="hibernate.c3p0.max_statements">0</prop>
                <prop key="hibernate.c3p0.idle_test_period">3000</prop>
            </props>
        </property>

        <property name="packagesToScan" value="com.swiggy.delivery.data.sql.entities" />

    </bean>

    <bean id="slaveTaskSeqAuditSessionFactory"
          class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" >

        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                <prop key="hibernate.show_sql">${hibernate.slave.taskSeqAudit.showSql}</prop>
                <prop key="hibernate.format_sql">${hibernate.slave.taskSeqAudit.formatSql}</prop>
                <prop key="hibernate.connection.url">${hibernate.slave.taskSeqAudit.connection.url}</prop>
                <prop key="hibernate.connection.driver_class">com.mysql.jdbc.Driver</prop>
                <prop key="hibernate.connection.username">${hibernate.slave.taskSeqAudit.connection.username}</prop>
                <prop key="hibernate.connection.password">${hibernate.slave.taskSeqAudit.connection.password}</prop>
                <prop key="hibernate.c3p0.min_size">${hibernate.slave.taskSeqAudit.c3p0.min_size}</prop>
                <prop key="hibernate.c3p0.max_size">${hibernate.slave.taskSeqAudit.c3p0.max_size}</prop>
                <prop key="hibernate.c3p0.timeout">300</prop>
                <prop key="hibernate.c3p0.max_statements">0</prop>
                <prop key="hibernate.c3p0.idle_test_period">3000</prop>
            </props>
        </property>

        <property name="packagesToScan" value="com.swiggy.delivery.data.sql.entities" />

    </bean>

    <bean id="transactionManager"
        class="org.springframework.orm.hibernate4.HibernateTransactionManager" primary="true">
        <property name="sessionFactory" ref="sessionFactory" />
        <property name="defaultTimeout" value="10" />
    </bean>
    <bean id="slaveTransactionManager"
        class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="slaveSessionFactory" />
        <property name="defaultTimeout" value="10" />
    </bean>

    <bean id="taskSeqAuditTransactionManager"
          class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="taskSeqAuditSessionFactory" />
        <property name="defaultTimeout" value="10" />
    </bean>
    <bean id="slaveTaskSeqAuditTransactionManager"
          class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="slaveTaskSeqAuditSessionFactory" />
        <property name="defaultTimeout" value="10" />
    </bean>

    <tx:annotation-driven transaction-manager="transactionManager" />
    <tx:annotation-driven transaction-manager="slaveTransactionManager"/>

    <tx:annotation-driven transaction-manager="taskSeqAuditTransactionManager"/>
    <tx:annotation-driven transaction-manager="slaveTaskSeqAuditTransactionManager"/>

2 个答案:

答案 0 :(得分:2)

假设您要在两个不同数据库中的表中写入(休眠)对象,那么有关springboot 1.4的旧教程可能对此有所帮助:

http://roufid.com/spring-boot-multiple-databases-configuration/

您将必须创建两个数据源,实体管理器和事务管理器。

欢呼

PS:我假设这是因为您的问题中没有特定的指示,并且在您描述的场景中,没有任何特定的代码问题的指示,任何日志和测试结果和/或错误消息均支持该指示。请记住,问题越具体,答案就越不明确。

答案 1 :(得分:0)

已确定问题所在。 TimeAuditDaoImplV2 中的方法 saveTimeAudit 存在问题,其中 Transactional 批注不正确。尽管我正在使用与DB Y的连接的sessionFactory,但默认情况下 Transactional 注释使用的是不属于DB Y会话的主事务管理器。 将注释修改为 @Transactional(value = "taskSeqAuditTransactionManager") 此修改确保DB Y的事务管理器与DB Y的sessionFactory一起使用,并且现在数据在两个DB(即X和Y)中都持久存在。 还从 TimeAuditServiceImpl 类的saveTimeAudit方法中删除了 Transactional 批注。