首先申明,吐槽我程框架仅仅是希望框架能够更好,不代表不热爱公司哈。其次,携程的DAL框架(类似MyBatis的orm框架)确实有点那个不太给力了,估计和以前都是C#语言为主的历史原因。
DAL框架不给力的地方如下:
1、不支持Spring事务,很多功能没法实现,例如在事务性的a()里想要执行一段非事务的逻辑;或者在事务性的a()里想要执行一段新起事务的逻辑,但是b的事务和a的事务互不干扰。这些在Spring事务重视非常容易做到的,但是dal事务却无能为力。
2、注解必须加在方法上,不支持加上类头上,对于一个类下面有非常多的原子性操作方法的情况需要重复写若干遍相同注解代码。
3、使用时候出现问题,框架部门远程指点,结果给我引向了偏路了。。。-_-||
本文主要分析3的问题,正确使用方式如下!
import org.springframework.context.annotation.Configuration;
import com.ctrip.platform.dal.dao.annotation.EnableDalTransaction;
@Configuration
@EnableDalTransaction
public class TransactionConfig {}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TransactionConfig.class)
public class DataOperateServiceTest {
@Autowired
DataOperateServiceImpl<DataCreateEntity> service;
@Test
public void doUpdateTest() throws Exception{
InitUpdateEntity updateData = new InitUpdateEntity();
updateData.setDataId(229);
updateData.setValue("init status edit 4000");
service.doUpdateHandle(updateData, authentication);
}
}
@Service
public class DataOperateServiceImpl<T> implements DataOperateService<T> {
private static final Logger log = LoggerFactory.getLogger(DataOperateServiceImpl.class);
/**
* Create a new Data.
*
* @param data
* @param authentication
* @throws
*/
@Override
@DalTransactional(logicDbName = "xxxx")
public TData doCreateHandle(DataCreateEntity data, Authentication authentication) throws SQLException,
InvocationTargetException, IllegalAccessException, DataExistException {
TData tData = new TData();
DataCondition dataCondition = new DataCondition();
BeanUtils.copyProperties(tData, data);
tData.setVersion(1);
tData.setStatus(StatusTransfer.initVisible());
BeanUtils.copyProperties(dataCondition, data);
dataCondition.setAppIds(Lists.newArrayList(data.getAppId()));
if (CollectionUtils.isEmpty(DaoManager.queryData(dataCondition))) {
tData = DaoManager.insert(Table.DATA, tData, authentication.getName());
} else {
log.error("The same data record has existed!");
throw new DataExistException("The same data record has existed!");
}
if (DaoManager.queryData(dataCondition).size() != 1) {
throw new DataExistException("The same data record has existed!");
}
return tData;
}
}
或者上面第二个代码片段改为(更推荐):
@RunWith(SpringRunner.class)
@SpringBootTest(properties = "spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration")
public class DataOperateServiceTest {
@Autowired
DataOperateServiceImpl<DataCreateEntity> service;
@Test
public void doUpdateTest() throws Exception{
InitUpdateEntity updateData = new InitUpdateEntity();
updateData.setDataId(229);
updateData.setValue("init status edit 4000");
service.doUpdateHandle(updateData, authentication);
}
}
CGLB动态代码出业务的service类,然后DalTransactional实现CGLB的方法拦截,当发生异常或者执行sql等任何一个方法时候都会触发下面的代码:
DalClientFactory.getClient(getLogicDbName(method)).execute(new DalCommand() {
@Override
public boolean execute(DalClient client) throws SQLException {
try {
result.set(proxy.invokeSuper(obj, args));
} catch (Throwable e) {
throw DalException.wrap(e);
}
return false;
}
}, hints);
然后会跳转到DalDirectClient类的execute方法,DalTransactionManager类中的doInTransaction方法如下
public <T> T doInTransaction(ConnectionAction<T> action, DalHints hints)throws SQLException{
action.initLogEntry(connManager.getLogicDbName(), hints);
action.start();
Throwable ex = null;
T result = null;
int level;
try {
level = startTransaction(hints, action);
action.populateDbMeta();
result = action.execute();
endTransaction(level);
} catch (Throwable e) {
action.error(e);
rollbackTransaction();
MarkdownManager.detect(action.connHolder, action.start, e);
}finally{
action.cleanup();
}
action.end(result);
return result;
}
当方法检测到throwable异常时候会执行 rollbackTransactional方法如下。
public void rollbackTransaction() throws SQLException {
if(rolledBack)
return;
beforeRollback();
rolledBack = true;
// Even the rollback fails, we still set the flag to true;
cleanup(false);
afterRollback();
}
目前这个beforeRollback和afterRollback啥事也没干。cleanup实现其实和所有的orm框架做的事情是一样的,都是是否做Connection.commit()还是做Connection.rollback()。如果是回滚,则执行的是tomcat的DisposableConnectionFacade类的invoke方法去实现回滚,具体代码如下:
private void cleanup(boolean commit) {
Connection conn = connHolder.getConn();
try {
if(commit)
conn.commit();
else
conn.rollback();
} catch (Throwable e) {
logger.error("Can not commit or rollback on current connection", e);
}
try {
conn.setAutoCommit(true);
} catch (Throwable e) {
e.printStackTrace();
}
connHolder.close();
DalTransactionManager.clearCurrentTransaction();
}
原理主要就是上述的大致样子。