这里先简单描述一下问题,
场景:在一个线程中创建了另外一个线程处理一部分业务,
主线程对数据库进行新增操作,在操作结束后,启动一个新的线程去执行查询插入的数据的查询操作,发现了数据没有查询的问题。
下面我们我们直接上代码:
@Service
@Transactional
@Slf4j
public class RPatientService extends BaseService {
@Autowired
private SequenceIdGenerator sequenceIdGenerator;
@Autowired
private WeChatMessageService weChatMessageService;
@Autowired
private RPatientMapper rPatientMapper;
@Autowired
private RReportIllnessService rReportIllnessService;
@Autowired
private ImSessionUserService imSessionUserService;
@Autowired
private RDoctorService rDoctorService;
public void registerPS(String openId, String doctorId, byte status) {
//按openId查询用户
synchronized (openId.intern()) {
RUser rUser = rUserService.getRUserByOpenId(openId);
RPatient rPatient = null;
if (rUser == null) {
//用户不存在插入用户
rUser = rUserService.createUserByOpenIdAndPS(openId, Constant.RUSER_TYPE_0_PATIENT, status);
//新增患者
rPatient = initPatient(rUser);
log.info("[RPatientService]:new patient join:{}", JsonUtil.toJson(rUser));
} else if (rUser.getType() == Constant.RUSER_TYPE_1_DOCTOR) {
//医生扫描医生二维码,不处理
return;
}
if (rPatient == null) {
rPatient = this.getPatientByUserId(rUser.getId());
if(rPatient == null){
log.error("[RPatientService]: patient not exist:{}", JsonUtil.toJson(rUser));
return ;
}
}
Long rDoctorId = null;
if (!StringUtils.isEmpty(doctorId)) {
//报到 关联医生和患者生成report
rDoctorId = Long.valueOf(doctorId);
RDoctor rDoctor = rDoctorService.getDoctorById(rDoctorId);
if(rDoctor == null){
log.error("[RPatientService]: doctor not exist:doctorId:{}", doctorId);
return;
}
rReportService.report(rDoctorId, rPatient.getId());
}
eventBus.post(new PatientEvent(rUser.getId(), rDoctorId));
//新
//this.registerEvent(new PatientEvent(rUser.getId(), rDoctorId));
}
}
//EventBus执行
@Subscribe
@AllowConcurrentEvents
public void registerEvent(PatientEvent patientEvent) {
log.info("[PatientEvent]:{}", JsonUtil.toJson(patientEvent));
Long rUserId = patientEvent.getrUserId();
if (rUserId == null) {
return;
}
RUser rUser = rUserService.selectByPrimaryKey(rUserId);
RPatient rPatient = this.getPatientByUserId(rUserId);
// RReport rReport = rReportService.getByPatientId(rPatient.getId());
if (StringUtils.isEmpty(rPatient.getPhone())) {
//提示用户身份验证
weChatMessageService.sendToIdentityVerificationKFMsgForPatient(rUser.getVxopenid());
} else if (patientEvent.getrDoctorId() != null) {
//创建会话
rReportService.createNewSession(rPatient.getId());
//已关联医生,提示选择疾病
weChatMessageService.sendToChoiceIllnessKFMsgForPatient(rUser.getVxopenid(), patientEvent.getrDoctorId());
}
}
}
新增操作的代码:
@Service
@Transactional
@Slf4j
public class RReportService extends BaseService {
@Autowired
private SequenceIdGenerator sequenceIdGenerator;
@Autowired
private RReportMapper rReportMapper;
@Autowired
private RPatientService patientService;
@Autowired
private ImSessionService imSessionService;
public RReport report(Long doctorId, Long patientId) {
RReport rReport = getByDoctorIdPatientId(doctorId, patientId);
if (rReport == null) {
rReport = initRReport(doctorId, patientId);
log.info("new report generate:{}", JsonUtil.toJson(rReport));
}
return rReport;
}
//插入数据
private RReport initRReport(Long doctorId, Long patientId) {
RReport rReport = new RReport();
rReport.setRdoctorid(doctorId);
rReport.setRpatientid(patientId);
rReport.setCreatetime(new Date());
insertOrUpdate(rReport);
return rReport;
}
//EventBus新事务执行的代码
public void createNewSession(Long patientId) {
RPatient rPatient = patientService.selectByPrimaryKey(patientId);
if (StringUtils.isEmpty(rPatient.getPhone())) {
log.info("患者没有完善个人信息,需要先完善个人信息!");
return;//未完善信息的不处理
}
RReportExample rReportExample = new RReportExample();
rReportExample.createCriteria().andRpatientidEqualTo(patientId).andImsessionidIsNull();
List rReportList = this.selectByExample(rReportExample);
log.info("查询隐患报道关系." + "数量只能有一条:" + rReportList.size() + "记录信息=" + rReportList.toString());
if (rReportList != null && rReportList.size() != 0) {
for (RReport rReport : rReportList) {
Long doctorId = rReport.getRdoctorid();
//创建会话
ImSession imSession = imSessionService.createNewSession(doctorId, patientId);
rReport.setImsessionid(imSession.getId());
this.insertOrUpdate(rReport);//更新会话ID
}
}
ImSessionExample imSessionExample = new ImSessionExample();
imSessionExample.createCriteria().andCreateuseridEqualTo(patientId).andSessiontypeEqualTo((byte) 2);
ImSession a = imSessionService.selectOneByExample(imSessionExample);
if (a == null) {
//创建客服
imSessionService.createNewSession2(424987751667077198L, patientId);
}
}
}
下面直接看我分析服务器打出一段日志就可以了
############这里创建了一个事务 35ea154e http-nio-8081-exec-10线程编号
2020-01-11 11:13:47.834 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | org.springframework.jdbc.datasource.DataSourceTransactionManager | Creating new transaction with name [com.framework.code.service.report.RPatientService.registerPS]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2020-01-11 11:13:47.836 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | org.springframework.jdbc.datasource.DataSourceTransactionManager | Acquired Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@5c225ae5 [wrapping: com.mysql.jdbc.JDBC4Connection@35ea154e]] for JDBC transaction
########切换JDBC事务并且交给Spring管理
2020-01-11 11:13:47.837 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | org.springframework.jdbc.datasource.DataSourceTransactionManager | Switching JDBC Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@5c225ae5 [wrapping: com.mysql.jdbc.JDBC4Connection@35ea154e]] to manual commit
############参与当前的事务 35ea154e
2020-01-11 11:13:47.837 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | org.springframework.jdbc.datasource.DataSourceTransactionManager | Participating in existing transaction
2020-01-11 11:13:47.838 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | com.framework.code.mapper.doctor.RUserMapper.selectByExample | ooo Using Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@5c225ae5 [wrapping: com.mysql.jdbc.JDBC4Connection@35ea154e]]
#######通过openid查询用户
2020-01-11 11:13:47.838 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | com.framework.code.mapper.doctor.RUserMapper.selectByExample | ==> Preparing: select 'true' as QUERYID, Id_, VxHeadUrl_, VxNickName_, VxSex_, VxOpenid_, VxProvince_, VxCity_, VxCountry_, Type_, PromotionSource_ from r_user WHERE ( VxOpenid_ = ? )
2020-01-11 11:13:47.839 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | com.framework.code.mapper.doctor.RUserMapper.selectByExample | ==> Parameters: oMGuv5uRcp03ycIgyjclwpZfr1Eg(String)
##########使用当前事务35ea154e
2020-01-11 11:13:47.842 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | com.framework.code.mapper.report.RPatientMapper.selectByExample | ooo Using Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@5c225ae5 [wrapping: com.mysql.jdbc.JDBC4Connection@35ea154e]]
#######根据用户Id查询患者信息
2020-01-11 11:13:47.842 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | com.framework.code.mapper.report.RPatientMapper.selectByExample | ==> Preparing: select 'true' as QUERYID, Id_, RUserId_, RegisterTime_, Name_, Phone_, CardNo_, Age_, Illness_, Irritability_, Inheritance_, IsBindFinished_, Sex_, VxHeadUrl_, PfksShopUserId_ from r_patient WHERE ( RUserId_ = ? )
2020-01-11 11:13:47.843 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | com.framework.code.mapper.report.RPatientMapper.selectByExample | ==> Parameters: 458690345937539120(Long)
#############这里表示使用AOP调用其他的事务,判断当前有事务,那么默认直接使用当前事务
2020-01-11 11:13:47.844 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | org.springframework.jdbc.datasource.DataSourceTransactionManager | Participating in existing transaction
############继续使用当前事务 35ea154e
2020-01-11 11:13:47.845 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | com.framework.code.mapper.doctor.RDoctorMapper.selectByExample | ooo Using Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@5c225ae5 [wrapping: com.mysql.jdbc.JDBC4Connection@35ea154e]]
#######通过医生Id查询医生信息
2020-01-11 11:13:47.845 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | com.framework.code.mapper.doctor.RDoctorMapper.selectByExample | ==> Preparing: select 'true' as QUERYID, Id_, RUserId_, Name_, NamePY_, RHospitalId_, RDepartmentId_, PositionCode_, Phone_, RegisterTime_, CheckStatus_, CheckTime_, CheckUserName_, CheckRemark_, Speciality_, Profile_, VxHeadUrl_, Dtype_, PfksShopUserId_, RDocCareType_ from r_doctor WHERE ( Id_ = ? )
2020-01-11 11:13:47.846 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | com.framework.code.mapper.doctor.RDoctorMapper.selectByExample | ==> Parameters: 463413442003472478(Long)
#############再次使用AOP调用其他的事务,判断当前有事务,那么默认使用当前事务
2020-01-11 11:13:47.849 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | org.springframework.jdbc.datasource.DataSourceTransactionManager | Participating in existing transaction
#########继续使用当前事务 35ea154e
2020-01-11 11:13:47.851 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | com.framework.code.mapper.report.RReportMapper.selectByExample | ooo Using Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@5c225ae5 [wrapping: com.mysql.jdbc.JDBC4Connection@35ea154e]]
#########通过医生ID和患者的ID查询医患报道表的信息
2020-01-11 11:13:47.852 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | com.framework.code.mapper.report.RReportMapper.selectByExample | ==> Preparing: select 'true' as QUERYID, Id_, RPatientId_, RDoctorId_, CreateTime_, IMSessionId_, IllnessName_, RDoctorNameRemark_, RPatientNameRemark_ from r_report WHERE ( RDoctorId_ = ? and RPatientId_ = ? )
2020-01-11 11:13:47.853 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | com.framework.code.mapper.report.RReportMapper.selectByExample | ==> Parameters: 463413442003472478(Long), 458690345958510641(Long)
##############继续使用当前事务 35ea154e
2020-01-11 11:13:47.854 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | com.framework.code.mapper.report.RReportMapper.insert | ooo Using Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@5c225ae5 [wrapping: com.mysql.jdbc.JDBC4Connection@35ea154e]]
#############添加医患报道表的一条记录 患者ID=458690345958510641 IMSessionId_是null
2020-01-11 11:13:47.857 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | com.framework.code.mapper.report.RReportMapper.insert | ==> Preparing: insert into r_report (Id_, RPatientId_, RDoctorId_, CreateTime_, IMSessionId_, IllnessName_, RDoctorNameRemark_, RPatientNameRemark_) values (?, ?, ?, ?, ?, ?, ?, ?)
2020-01-11 11:13:47.858 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | com.framework.code.mapper.report.RReportMapper.insert | ==> Parameters: 481834561777569793(Long), 458690345958510641(Long), 463413442003472478(Long), 2020-01-11 11:13:47.853(Timestamp), null, null, null, null
###########这里输出的日志
2020-01-11 11:13:47.861 | myHost | INFO | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | com.framework.code.service.report.RReportService | new report generate:{"id":481834561777569793,"rpatientid":458690345958510641,"rdoctorid":463413442003472478,"createtime":1578712427853,"imsessionid":null,"illnessname":null,"rdoctornameremark":null,"rpatientnameremark":null}
########提交启动的事务
2020-01-11 11:13:47.861 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | org.springframework.jdbc.datasource.DataSourceTransactionManager | Initiating transaction commit
#########这里我们又需要创建一个新的事务 7fb1091a 线程编号pool-1-thread-7
2020-01-11 11:13:47.861 | myHost | DEBUG | pool-1-thread-7 | [] | org.springframework.jdbc.datasource.DataSourceTransactionManager | Creating new transaction with name [com.framework.code.service.report.RPatientService.registerEvent]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
########之前线程的事务进行提交
2020-01-11 11:13:47.861 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | org.springframework.jdbc.datasource.DataSourceTransactionManager | Committing JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@5c225ae5 [wrapping: com.mysql.jdbc.JDBC4Connection@35ea154e]]
################这里是我们新的事务 7fb1091a
2020-01-11 11:13:47.862 | myHost | DEBUG | pool-1-thread-7 | [] | org.springframework.jdbc.datasource.DataSourceTransactionManager | Acquired Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@75935acf [wrapping: com.mysql.jdbc.JDBC4Connection@7fb1091a]] for JDBC transaction
2020-01-11 11:13:47.862 | myHost | DEBUG | pool-1-thread-7 | [] | org.springframework.jdbc.datasource.DataSourceTransactionManager | Switching JDBC Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@75935acf [wrapping: com.mysql.jdbc.JDBC4Connection@7fb1091a]] to manual commit
######这里是日志输出
2020-01-11 11:13:47.862 | myHost | INFO | pool-1-thread-7 | [] | com.framework.code.service.report.RPatientService | [PatientEvent]:{"rUserId":458690345937539120,"rDoctorId":463413442003472478}
##########使用当前事务
2020-01-11 11:13:47.862 | myHost | DEBUG | pool-1-thread-7 | [] | com.framework.code.mapper.doctor.RUserMapper.selectByPrimaryKey | ooo Using Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@75935acf [wrapping: com.mysql.jdbc.JDBC4Connection@7fb1091a]]
##########通过用户Id查询用户信息
2020-01-11 11:13:47.862 | myHost | DEBUG | pool-1-thread-7 | [] | com.framework.code.mapper.doctor.RUserMapper.selectByPrimaryKey | ==> Preparing: select Id_, VxHeadUrl_, VxNickName_, VxSex_, VxOpenid_, VxProvince_, VxCity_, VxCountry_, Type_, PromotionSource_ from r_user where Id_ = ?
2020-01-11 11:13:47.863 | myHost | DEBUG | pool-1-thread-7 | [] | com.framework.code.mapper.doctor.RUserMapper.selectByPrimaryKey | ==> Parameters: 458690345937539120(Long)
######之前的线程释放JDBC连接
2020-01-11 11:13:47.867 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | org.springframework.jdbc.datasource.DataSourceTransactionManager | Releasing JDBC Connection
[com.mchange.v2.c3p0.impl.NewProxyConnection@5c225ae5 [wrapping: com.mysql.jdbc.JDBC4Connection@35ea154e]] after transaction
2020-01-11 11:13:47.867 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | org.springframework.jdbc.datasource.DataSourceUtils | Returning JDBC Connection to DataSource
2020-01-11 11:13:47.867 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | org.springframework.web.servlet.DispatcherServlet | Completed 200 OK
####被AOP的切面拦截输入的日志
2020-01-11 11:13:47.867 | myHost | INFO | pool-1-thread-8 | [] | com.framework.web.eventbus.LogSubscribe | [LogSubscribe]:{"threadName":"http-nio-8081-exec-10","className":"com.framework.code.controller.webchat.WeChartController","methodName":"receiveEvent","argsMap":{"request":"ServletRequest","response":"ServletResponse"},"argsJson":"{\"request\":\"ServletRequest\",\"response\":\"ServletResponse\"}","happenTime":1578712427867,"throwable":null,"throwableString":null,"traceId":"436ed24f998d411ab5150b1258b22cd0","result":null,"useTime":33}
2020-01-11 11:13:47.867 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | org.springframework.session.web.http.SessionRepositoryFilter.SESSION_LOGGER | No session found by id: Caching result for getSession(false) for this HttpServletRequest.
########使用当前事务 7fb1091a
2020-01-11 11:13:47.867 | myHost | DEBUG | pool-1-thread-7 | [] | com.framework.code.mapper.report.RPatientMapper.selectByExample | ooo Using Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@75935acf [wrapping: com.mysql.jdbc.JDBC4Connection@7fb1091a]]
###通过用户ID查询患者信息
2020-01-11 11:13:47.867 | myHost | DEBUG | pool-1-thread-7 | [] | com.framework.code.mapper.report.RPatientMapper.selectByExample | ==> Preparing: select 'true' as QUERYID, Id_, RUserId_, RegisterTime_, Name_, Phone_, CardNo_, Age_, Illness_, Irritability_, Inheritance_, IsBindFinished_, Sex_, VxHeadUrl_, PfksShopUserId_ from r_patient WHERE ( RUserId_ = ? )
2020-01-11 11:13:47.869 | myHost | DEBUG | pool-1-thread-7 | [] | com.framework.code.mapper.report.RPatientMapper.selectByExample | ==> Parameters: 458690345937539120(Long)
##########使用AOP调用其他的事务,判断当前有事务,那么默认使用当前事务 7fb1091a
2020-01-11 11:13:47.870 | myHost | DEBUG | pool-1-thread-7 | [] | org.springframework.jdbc.datasource.DataSourceTransactionManager | Participating in existing transaction
2020-01-11 11:13:47.876 | myHost | DEBUG | pool-1-thread-7 | [] | com.framework.code.mapper.report.RPatientMapper.selectByPrimaryKey | ooo Using Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@75935acf [wrapping: com.mysql.jdbc.JDBC4Connection@7fb1091a]]
######通过用户ID查询患者信息
2020-01-11 11:13:47.876 | myHost | DEBUG | pool-1-thread-7 | [] | com.framework.code.mapper.report.RPatientMapper.selectByPrimaryKey | ==> Preparing: select Id_, RUserId_, RegisterTime_, Name_, Phone_, CardNo_, Age_, Illness_, Irritability_, Inheritance_, IsBindFinished_, Sex_, VxHeadUrl_, PfksShopUserId_ from r_patient where Id_ = ?
2020-01-11 11:13:47.876 | myHost | DEBUG | pool-1-thread-7 | [] | com.framework.code.mapper.report.RPatientMapper.selectByPrimaryKey | ==> Parameters: 458690345958510641(Long)
##########使用当前事务 7fb1091a
2020-01-11 11:13:47.878 | myHost | DEBUG | pool-1-thread-7 | [] | com.framework.code.mapper.report.RReportMapper.selectByExample | ooo Using Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@75935acf [wrapping: com.mysql.jdbc.JDBC4Connection@7fb1091a]]
##########通过患者ID查询患者报道信息表 458690345958510641
2020-01-11 11:13:47.878 | myHost | DEBUG | pool-1-thread-7 | [] | com.framework.code.mapper.report.RReportMapper.selectByExample | ==> Preparing: select 'true' as QUERYID, Id_, RPatientId_, RDoctorId_, CreateTime_, IMSessionId_, IllnessName_, RDoctorNameRemark_, RPatientNameRemark_ from r_report WHERE ( RPatientId_ = ? and IMSessionId_ is null )
2020-01-11 11:13:47.878 | myHost | DEBUG | pool-1-thread-7 | [] | com.framework.code.mapper.report.RReportMapper.selectByExample | ==> Parameters: 458690345958510641(Long)
########输出查询结果
2020-01-11 11:13:47.879 | myHost | INFO | pool-1-thread-7 | [] | com.framework.code.service.report.RReportService | 查询隐患报道关系.数量只能有一条:0记录信息=[]
看完日志 我们不难发现,在主线程明明已经执行完成新增操作,并且已经释放了JDBC连接,在EventBus的线程中新的事务中并没有查询到我们要插入的数据。。。。
按照正常的逻辑思考这就是一个死循环的。。。。。。明明我已经执行完成了新增操作并且释放了连接到连接池 。。问什么我在新的事务中查询不到数据呢。
个人的分析可能数据库出来也许一个短暂 的时间,因为主线程插入数据的时间和EventBus线程去查询间隔时间只有0.02秒的时间可能数据并没有写入完成操作的。我的解决方案就是放弃EventBus使用主线程一次处理所有业务,让一次接口调用的响应时间加长,来减少并发出现的问题。