我在工作时提供整体服务。它连接到单个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"/>
答案 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 批注。