Hibernate-Spring kullanan bir uygulamada database'den gelen exceptionları ortak bir hale getirip database-bağımsız şekilde takip etmek için doğru yöntem ne olmalı? JDBC-Hibernate exceptionlarını Spring exceptionlarına dönüştürme yöntemi uygulanabilirmi?

soruldu: 23 Ağu '13, 01:25

bts's gravatar image

bts
41224
cevap kabul oranı: 0%

Dediğiniz şekilde bir implementasyon yaptım ancak exception oluştuğunda o metoda hiç girmedi.

@Component
public class TestPersistenceExceptionTranslator implements PersistenceExceptionTranslator {

@Override
    public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
        System.out.println("XXXXXXXXXXXXXXXXXXXXXXX");
        return null;
    }

}

org.springframework.dao.DataIntegrityViolationException: could not delete: [core.domain.Test#1098323]; SQL [delete from test.test where id=? and versiyon=?]; constraint [TEST.FK_TEST_BORC_ODEME]; nested exception is org.hibernate.exception.ConstraintViolationException: could not delete: [core.domain.Test#1098323]
    at org.springframework.orm.hibernate3.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:637)
    at org.springframework.orm.hibernate3.HibernateTransactionManager.convertHibernateAccessException(HibernateTransactionManager.java:793)
    at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:664)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
    at org.springframework.test.context.transaction.TransactionalTestExecutionListener$TransactionContext.endTransaction(TransactionalTestExecutionListener.java:515)
    at org.springframework.test.context.transaction.TransactionalTestExecutionListener.endTransaction(TransactionalTestExecutionListener.java:290)
    at org.springframework.test.context.transaction.TransactionalTestExecutionListener.afterTestMethod(TransactionalTestExecutionListener.java:183)
    at org.springframework.test.context.TestContextManager.afterTestMethod(TestContextManager.java:406)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:90)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: org.hibernate.exception.ConstraintViolationException: could not delete: [core.domain.Test#1098323]
    at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:96)
    at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
    at org.hibernate.persister.entity.AbstractEntityPersister.delete(AbstractEntityPersister.java:2728)
    at org.hibernate.persister.entity.AbstractEntityPersister.delete(AbstractEntityPersister.java:2911)
    at org.hibernate.action.EntityDeleteAction.execute(EntityDeleteAction.java:97)
    at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:273)
    at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:265)
    at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:189)
    at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
    at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:51)
    at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1216)
    at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:383)
    at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:133)
    at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:656)
    ... 25 more
Caused by: java.sql.SQLIntegrityConstraintViolationException: ORA-02292: integrity constraint (TEST.FK_TEST_BORC_ODEME) violated - child record found

at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:445)
    at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:396)
    at oracle.jdbc.driver.T4C8Oall.processError(T4C8Oall.java:879)
    at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:450)
    at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:192)
    at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:531)
    at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:207)
    at oracle.jdbc.driver.T4CPreparedStatement.executeForRows(T4CPreparedStatement.java:1044)
    at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1329)
    at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3584)
    at oracle.jdbc.driver.OraclePreparedStatement.executeUpdate(OraclePreparedStatement.java:3665)
    at oracle.jdbc.driver.OraclePreparedStatementWrapper.executeUpdate(OraclePreparedStatementWrapper.java:1352)
    at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeUpdate(NewProxyPreparedStatement.java:105)
    at org.hibernate.persister.entity.AbstractEntityPersister.delete(AbstractEntityPersister.java:2710)
    ... 36 more
(13 Eyl '13, 01:59) bts bts's gravatar image

Yeni implementasyonun Spring tarafindan bulunabilmesi icin asagidaki satiri eklemeniz lazim. @Component anotasyonu tasiyan tüm siniflar Spring bean olarak sisteme dahil edilir.

<context:component-scan base-package="com.kurumsaljava"/>

(13 Eyl '13, 02:04) özcanacar ♦♦ %C3%B6zcanacar's gravatar image

Bean oluşturmada bir sorun yok, tüm sınıflarımız base package'ın altında. Constructor ekleyerek test ettiğimde bean'in oluştuğunu görebiliyorum.

(13 Eyl '13, 02:34) bts bts's gravatar image

O durumda translateExceptionIfPossible() metoduna atlamasi lazim. Atlamiyor ise, konfigürasyonda bir sorun var demektir.

(13 Eyl '13, 04:59) özcanacar ♦♦ %C3%B6zcanacar's gravatar image

Su anda üzerinde calistigim Pratik Spring isimli kitabimin Spring ve Hibernate isimli bölümünden alintidir:

Herhangi bir hata olusmasi durumunda Hibernate org.hibernate.HibernateException ve türevleri ile kullanicisini haberdar edecektir.

alt text

Veri katmaninda olusan hatalarin birebir servis katmanina yönlendirilmesi servis katmanini kullanilan ORM teknolojisine bagimli kilar. Bizim örnegimizde bu RentalServiceImpl sinifinin HibernateException sifindan bir hatayi yakalamasi ve gerekli islemleri yapmasi anlamina gelmektedir. Bunu engellemek icin veri katmaninda olusan Hibernate hatalari yakalanarak, servis katmaninin kullanabilecegi türde hatalara dönüstürülebilir. Bu gerekli exception siniflarinin olusturulmasi, Hibernate hatalarinin bu hatalara dönüstürülmesi ve yeni hatalarin servis katmanina yönlendirilmesi anlamina gelmektedir. Bu tür bir hata yönetimini Spring bizim icin daha iyi yapabilir.

Spring olusan Hibernate hatalarinin baska hata siniflarina dönüstürülmesini (exception translation) AOP yardimi ile mümkün kilmaktadir. Veri katmaninda olusan tüm hatalar Spring'in ihtiva ettigi org.springframework.dao.DataAccessException ve türevlerine otomatik olarak dönüstürülebilir. Bu amacla @Repository anotasyonu kullanilabilir.


Kod 7.9 - HibernateCustomerRepositoryImpl 

@Repository
public class HibernateCustomerRepositoryImpl implements CustomerRepository {

    private SessionFactory sessionFactory;

    @Override
    public Customer getCustomerByName(String name) {
        Query query = getCurrentSession().createQuery(
                "from Customer c where c.name=:name");
        query.setString("name", name);
        return (Customer) query.uniqueResult();

    }

    @Override
    public void save(Customer customer) {
        getCurrentSession().save(customer);
    }
}

Kod 7.9'da @Repository anotasyonunun kullanim sekli yer almaktadir. Olusan Hibernate hatalarinin otomatik olarak org.springframework.dao.DataAccessException ve türevlerine dönüstürülebilmesi icin XML konfigürasyon dosyasinda bir PersistenceExceptionTranslationPostProcessor tanimlamasi yapilmasi gerekmektedir. Bu tanimlama kod 7.10'da yer almaktadir.


Kod 7.10 - applicationContext.xml 

<bean class="org.springframework.dao.annotation.
    PersistenceExceptionTranslationPostProcessor"/>

@Repository anotasyonu kullaniminin mümkün olmadigi durumlarda (örnegin kaynak kodun olmamasi) hata dönüsümü icin gerekli konfigürasyon kod 7.11'deki sekilde XML dosyasinda yapilabilir.


Kod 7.11 - applicationContext.xml 

<bean id="persistenceExceptionInterceptor" class="org.springframework.dao.support.
    PersistenceExceptionTranslationInterceptor"/>

<aop:config>
    <aop:advisor pointcut="execution(* *..Repository+.*(..))"
        advice-ref="persistenceExceptionInterceptor"/>
</aop:config>
permanent link

cevaplandı: 23 Ağu '13, 02:13

%C3%B6zcanacar's gravatar image

özcanacar ♦♦
17.2k59183183
cevap kabul oranı: 52%

Kitabınızı sabırsızlıkla bekliyoruz.

(23 Ağu '13, 02:45) omerozkan omerozkan's gravatar image

Özcan hocam kitabınızın çıkış tarihi tahimini olarak belli mi?

(23 Ağu '13, 02:56) jackk jackk's gravatar image
2

Yil sonuna kadar bitirmeyi düsünüyorum.

(23 Ağu '13, 04:16) özcanacar ♦♦ %C3%B6zcanacar's gravatar image

Önerdiğiniz yöntem işe yarıyor. Context'e eklediğimiz bean tanımından sonra exception hiyerarşisinde spring exceptionları da yer alıyor. Ancak detaylı sql exceptionları ile ilgili bir problem var. Şuanki yapımızda, sql exceptionlarını parse edip dönen sql code değerlerine göre kullanıcı-dostu hata mesajları gösterebiliyoruz. Örneğin;

ORA-02292: integrity constraint (TEST3.FK_BORC_ODEME) violated - child record found
"İlişkili Kayıt Bulundu. Ödemesi bulunan borç silinemez."

Yaptığımız testlerde, sql exception'dan başlayıp yukarıya doğru hibernate exception ve spring exception olarak oluşan exceptionda, sql exception'daki sql code bilgisinin exception cause'una taşınmadığını gördük. Bu sebeple de sql code değerine göre kullanıcıya gösterdiğimiz hata mesajlarında sorun yaşıyoruz.

Caused by: org.springframework.orm.hibernate3.HibernateJdbcException: JDBC exception on Hibernate data access: SQLException for SQL [update test3.genel set karar_no=? where id=?]; SQL state [72000]; error code [12899]; could not update: [core.domain.Genel#217]; nested exception is org.hibernate.QueryTimeoutException: could not update: [core.domain.Genel#217]

Caused by: org.hibernate.QueryTimeoutException: could not update: [core.domain.Genel#217]

Caused by: java.sql.SQLException: ORA-12899: value too large for column "TEST3"."GENEL"."KARAR_NO" (actual: 135, maximum: 50)

Caused by: org.springframework.dao.DataIntegrityViolationException: Could not execute JDBC batch update; SQL [insert into test3.genel (adi, id) values (?,?)]; constraint [TEST3.SYS_C0018605]; nested exception is org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update

Caused by: org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update

Caused by: java.sql.BatchUpdateException: ORA-00001: unique constraint (TEST3.SYS_C0018605) violated


Caused by: org.springframework.dao.DataIntegrityViolationException: could not delete: [core.domain.Borc#50401]; SQL [delete from test3.borc where id=? ]; constraint [TEST3.FK_BORC_ODEME]; nested exception is org.hibernate.exception.ConstraintViolationException: could not delete: [core.domain.Borc#50401]

Caused by: org.hibernate.exception.ConstraintViolationException: could not delete: [core.domain.Borc#50401]

Caused by: java.sql.SQLIntegrityConstraintViolationException: ORA-02292: integrity constraint (TEST3.FK_BORC_ODEME) violated - child record found

Exception handler'ımızı spring exceptionlarını baz alacak şekilde düzenlediğimizde, exception hiyerarşisinde dolaşmadan, database'den gelen sql code değerlerine ulaşma imkanı varmı?

(10 Eyl '13, 02:43) bts bts's gravatar image

Yeni bir PersistenceExceptionTranslator implementasyonu olusturmaniz yeterli olacaktir. Spring otomatik olarak bu implementasyonu kullanilir. translateExceptionIfPossible() metodunda orjinal exception nesnesini kullanarak, olusan SQL hatalarini olusturacaginiz yeni DataAccessException nesnesine aktarabilirsiniz.

@Component public class CustomPersistenceExceptionTranslator implements PersistenceExceptionTranslator {

@Override
public DataAccessException translateExceptionIfPossible(final RuntimeException ex) {
    return null;
}

}

(12 Eyl '13, 10:26) özcanacar ♦♦ %C3%B6zcanacar's gravatar image
Cevabınız
toggle preview

powered by BitNami OSQA