Documente Academic
Documente Profesional
Documente Cultură
0
Publicado:Martes,10Febrero200903:00
Elprocesamientodetransaccionesdeberalograrunaltogradodeintegridadyconsistenciadedatos.En
esteartculoveremosalgunoserrorescomunesalusartransaccionesenJava.UsaremosejemplosconSpring
FrameworkyEnterpriseJavaBeans(EJB)3.0parademostrarelusodeunaestrategiatransaccionalefectiva.
LastransaccionesnosiempreparecenfuncionarcomoseesperaenlaplataformaJava.Acontinuacinveremosporquocurreesto,
conejemplosdecdigoyerrorescomunesquesecomentenenlamayoradelosentornosproductivos.
ParalosejemplosdecdigovamosausarSpringFramework(versin2.5),perolosconceptosdetransaccionessonlosmismospara
laespecificacinEJB3.0.Enlamayoradeloscasos,esslocuestindereemplazarlaanotacindeSpringFramework
@Transactional conlaanotacindeEJB3.0 @TransactionAttribute .Enloscasosesquehayadiferenciasentreestosframeworks,
vamosaincluirejemplosparaambos.
@Stateless
publicclassTradingServiceImplimplementsTradingService{
@ResourceSessionContextctx;
@Resource(mappedName="java:jdbc/tradingDS")DataSourceds;
publiclonginsertTrade(TradeDatatrade)throwsException{
ConnectiondbConnection=ds.getConnection();
try{
Statementsql=dbConnection.createStatement();
Stringstmt=
"INSERTINTOTRADE(ACCT_ID,SIDE,SYMBOL,SHARES,PRICE,STATE)"
+"VALUES('"
+trade.getAcct()+"','"
+trade.getAction()+"','"
+trade.getSymbol()+"',"
+trade.getShares()+","
+trade.getPrice()+",'"
+trade.getState()+"')";
sql.executeUpdate(stmt,Statement.RETURN_GENERATED_KEYS);
ResultSetrs=sql.getGeneratedKeys();
if(rs.next()){
returnrs.getBigDecimal(1).longValue();
}else{
thrownewException("TradeOrderInsertFailed");
}
}finally{
if(dbConnection!=null)dbConnection.close();
}
}
}
publicTradeDataplaceTrade(TradeDatatrade)throwsException{
try{
insertTrade(trade);
updateAcct(trade);
returntrade;
}catch(Exceptionup){
//logdelerror
throwup;
}
}
publicclassTradingServiceImpl{
publicclassTradingServiceImpl{
@PersistenceContext(unitName="trading")EntityManagerem;
publiclonginsertTrade(TradeDatatrade)throwsException{
em.persist(trade);
returntrade.getTradeId();
}
}
@Transactional
de Spring Framework
publicclassTradingServiceImpl{
@PersistenceContext(unitName="trading")EntityManagerem;
@Transactional
publiclonginsertTrade(TradeDatatrade)throwsException{
em.persist(trade);
returntrade.getTradeId();
}
}
Volvemosaprobarelcdigo,ysiguesinfuncionar.ElproblemaesquehayquedecirleaSpringqueestamosuandoanotacionespara
gestionarlastransacciones.Amenosqueestemoshaciendounapruebaunitariacompleta,esteerrorsueleserdificildedescubrir,y
hacequelosdesarrolladoresterminenaadiendolalgicadetransaccionesenlosarchivosdeconfiguracindeSpringenvezde
usaranotaciones.
Cuandoseusalaanotacin @Transactional deSpring,debemosagregarlalneasiguienteanuestroarchivodeconfiguracinde
Spring:
<tx:annotationdriventransactionmanager="transactionManager"/>
@Transactional
@Transactional(readOnly=true,propagation=Propagation.SUPPORTS)
publiclonginsertTrade(TradeDatatrade)throwsException{
//cdigoJDBCac...
}
Sabsquocurre?Aver,vamosconunasopciones.Cuandoseejecutaelmtododelejemploanterior:
A.Selanzaunaexcepcinindicandoqueesunaconexindeslolectura
B.Seinsertacorrectamentelaordendecompraysehaceelcommit
C.Nosehacenadaporqueelniveldepropagacinesten SUPPORTS
Terends?LaopcincorrectaesB.Laordendecompraseinsertacorrectamenteenlabasededatos,inclusoaunqueelflagde
slolecturaestabaen true yelniveldepropagacinen SUPPORTS .Cmopuedeser?Noseiniciningnatransaccinporqueel
mododepropagacinera SUPPORTS ,asqueelmtodouselmododetransaccioneslocales(debasededatos).Yelflagdeslo
lecturasloaplicasiseiniciaunatransaccin.Enestecaso,noseiniciningunatransaccin,asqueseignorelflag.
@Transactional(readOnly=true,propagation=Propagation.REQUIRED)
publiclonginsertTrade(TradeDatatrade)throwsException{
//cdigoJDBCac...
}
slosevanaleerdatos?Larespuestaesque,enrealidad,nosenecesita.Iniciarunatransaccinpararealizaroperacionesdeslo
lecturaagregarprocesamientoextra,ypuedeocasionarbloqueosdelecturacompartidaenlabasededatos(dependiendodeltipode
basededatosydelniveldeaislamientoqueseuse).Enresumen,elflagdeslolecturanotienemuchosentidocuandoseusa
persistenciausandoJDBC,yocasionaprocesamientoextracuandoseiniciaunatransaccininnecesaria.
@Transactional(readOnly=true,propagation=Propagation.REQUIRED)
publiclonginsertTrade(TradeDatatrade)throwsException{
em.persist(trade);
returntrade.getTradeId();
}
Cuandoseejecutaelmtodo insertTrade() :
A.Selanzaunaexcepcinindicandoqueesunaconexindeslolectura
B.Seinsertacorrectamentelaordendecompraysehaceelcommit
C.Nosehacenadaporqueelflagdeslolecturaesten true
LaopcincorrectaeslaB.Laordendecompraseinsertaenlabasededatossinningnerror.Averunminutoelejemploanterior
nosmostrqueselanzaunaexcepcinporconexindeslolecturacuandoelmododepropagacinera REQUIRED .Estoescierto
cuandoseusaJCBC.CuandoseusaunframeworkORM,elflagdeslolecturaessloun"consejo"paralabasededatosyuna
directivaparaelORM(enestecaso,Hibernate)paraestablecerelmododeflushdelcachdeobjetosa NEVER (Nunca),indicando
queelcachdeobjetosnodebesincronizarseconlabasededatosduranteestaunidaddetrabajo.Sinembargo,elmodode
propagacin REQUIRED tieneprecedenciasobretodoesto,permitiendoalatransaccininiciaryfuncionarcomosinoestuvieraelflag
deslolectura.
UsodeslolecturaenJPA
Loquenosllevaaotroerrorfrecuente.Sabiendotodoesto,culsuponsqueserelresultadodeejecutarelsiguientecdigo,slo
asignandoelvalordelflagdeslolectura?
@Transactional(readOnly=true)
publicTradeDatagetTrade(longtradeId)throwsException{
returnem.find(TradeData.class,tradeId);
}
Elmtodo getTrade() :
A.Iniciaunatransaccin,obtienelaordendecompra,yluegohacecommitdelatransaccin
B.Obtienelaordendecomprasininiciarunatransaccin
LarespuestacorrectaeslaA.Seiniciaunatransaccinysehaceuncommit.Nonosolvidemosqueelmododepropagacinpor
defectodelaanotacin @Transactional es REQUIRED .Estosignificaqueseiniciaunatransaccincuando,enrealidad,nose
necesitabadeuna.Dependiendodelabasededatosqueusemos,estopuedeocasionarbloqueoscompartidosinnecesarios,loque
puederesultarensituacionesdedeadlockenlabasededatos.Adems,seconsumetiempodeprocesamientoyrecursos
innecesariosparainiciarydetenerlatransaccin.
Enresumen,cuandoseusaunframeworkORM,elflagdeslolecturaespracticamenteinutil,yenlamayoradeloscasosseignora.
Perosivamosainsitirconsuuso,siempretenemosqueestablecerelmododepropagacina SUPPORTS ,demaneraquenoseinicie
unatransaccin,comoenelejemplosiguiente:
@Transactional(readOnly=true,propagation=Propagation.SUPPORTS)
publicTradeDatagetTrade(longtradeId)throwsException{
returnem.find(TradeData.class,tradeId);
}
publicTradeDatagetTrade(longtradeId)throwsException{
returnem.find(TradeData.class,tradeId);
}
REQUIRES_NEW
@Transactional(propagation=Propagation.REQUIRES_NEW)
@Transactional(propagation=Propagation.REQUIRES_NEW)
publiclonginsertTrade(TradeDatatrade)throwsException{...}
@Transactional(propagation=Propagation.REQUIRES_NEW)
publicvoidupdateAcct(TradeDatatrade)throwsException{...}
Ambosmtodossonpblicos,porloquepuedenserinvocadosdeformaindependienteunodelotro.Losproblemassurgenconel
atributo REQUIRES_NEW cuandohaymtodosquelosusandentrodelamismaUnidadLgicadeTrabajoatravsdecomunicacin
entreservicios,ousandoorquestacin.Porejemplo,supongamosqueinvocamosalmtodo updateAcct() deformaindependiente
decualquierotromtodoenalgunoscasosdeuso,perotambinhayuncasodondeelmtodoseinvocadentrodelmtodo
insertTrade() .Ahorabien,siocurreunaexcepcinluegodelainvocacina updateAcct() ,laordendecompravaatenerun
rollback,perosevaarealizaruncommitdelaactualizacinalacuenta.Porejemplo:
@Transactional(propagation=Propagation.REQUIRES_NEW)
publiclonginsertTrade(TradeDatatrade)throwsException{
em.persist(trade);
updateAcct(trade);
//Unaexcepcinocurreac!Laordendecompratieneunrollback,
//perolaactualizacindelacuentano!
...
}
@Transactional(propagation=Propagation.REQUIRED)
publicTradeDataplaceTrade(TradeDatatrade)throwsException{
try{
insertTrade(trade);
updateAcct(trade);
returntrade;
}catch(Exceptionup){
//logdelerror
throwup;
}
}
Supongamosquelacuentanotienefondossuficientespararealizarlaoperacinencuestin,onoesthabilitadatodava,ylanza
unaexcepcinchequeada(porejemplo,una FundsNotAvailableException ).Sepersistelaordendecomprasenlabasededatos,o
sesehaceunrollbackdetodalaunidaddetrabajo?Larespuesta,sorprendentemente,esquesobreunaexcepcinchequeada
(tantoenSpringFrameworkcomoenEJB),sehacecommitsobrelatransaccin.Estosignificaquesiocurreunaexcepcin
chequeadadurantelaejecucindelmotdo updateAcct() ,laordendecompraigualserpersistida,perolacuentanovaaestar
actualizadareflejandolaoperacin.
Quizsestasealaprincipalcausadelosproblemasdeintegridadyconsistenciadedatos.LasexcepcionesdeRuntime(esdecir,las
nochequeadas)fuerzanunrollbackautomticosobretodalaunidaddetrabajo,perolasexcepcioneschequeadasno.Esporesto
que,enelcdigoanterior,latransaccinesinutilapesardequepareceusarunatransaccinparamanteneratomicidady
consistencia,enlarealidadnolohace.
Aunqueestecomportamientopuedaparecerextrao,hayvariosbuenosmotivosparaquelastransaccionesfuncionenas.Primero,
notodaslasexcepcioneschequeadasson"malas"puedenusarseparaidentificacindeeventosopararedirigirelprocesamiento
basndoseenciertascondiciones.Peroadems,elcdigodelaaplicacinpodratomarmedidascorrectivasenalgunasexcepciones
chequeadas,ypermitirquelatransaccinsecomplete.Porejemplo,consideremoselescenarioendondeescribimoscdigoparaun
sitiodeventadelibros.Paracompletarlaordendecompradelibros,necesitamosenviarunemaildeconfirmacincomopartedel
proceso.Sielservidordemailestcaido,selanzaralgntipodeexcepcinchequeadaindicandoqueelmensajenopudoenviarse.
Silasexcepcioneschequeadascausaranunrollbackautomtico,todalaordendecompratendraunrollbackporqueelservidorde
mailestcaido.Alnorealizarunrollbackautomticosobreexcepcioneschequeadas,podemosatraparlaexcepcinyrealizaralguna
accincorrectiva(comoenviarelmensajeaunacoladependientes),yrealizaruncommitdelrestodelaorden.
accincorrectiva(comoenviarelmensajeaunacoladependientes),yrealizaruncommitdelrestodelaorden.
CuandoseusaunmodelotransaccionalDeclarativo,debemosindicarcmoelcontenedoroelframeworkvaamanejaralas
excepciones.EnSpringFrameworkespecificamoselparmetro rollbackFor enlaanotacin @Transactional ,comovemosa
continuacin:
@Transactional(propagation=Propagation.REQUIRED,rollbackFor=Exception.class)
publicTradeDataplaceTrade(TradeDatatrade)throwsException{
try{
insertTrade(trade);
updateAcct(trade);
returntrade;
}catch(Exceptionup){
//logdelerror
throwup;
}
}
@TransactionAttribute(TransactionAttributeType.REQUIRED)
publicTradeDataplaceTrade(TradeDatatrade)throwsException{
try{
insertTrade(trade);
updateAcct(trade);
returntrade;
}catch(Exceptionup){
//logdelerror
sessionCtx.setRollbackOnly();
throwup;
}
}
}
Conclusin
ElcdigoqueseusaparaimplementartransaccionesesJavanoesmuycomplejosinembargo,puederesultarcomplejosu
utilizacin.HayvarioserrorescomunesasociadosconlaimplementacindelsoportetransaccionalenJava.Elmayorproblemaesque
nohayningnavisodelcompiladoroentiempodeejecucindequelaimplementacindelatransaccinesincorrecta.Msan,
implementarelsoportedetransaccionesnoesslounejerciciodecodificacin.Sedebededicarunesfuerzoimportanteaestablecer
laestrategiatransaccionalgeneral.
BasadoenTransactionstrategies:Understandingtransactionpitfalls