I am trying to use JPA for persistence in a Java Web Application for a RESTful WebService. The program runs through the EntityManager, persisting an Entity class, being surrounded bz a transaction (begin and commit). The transaction makes troubles though. When I check the MySQL database the necessary tables are created, but no entries inside of them. How can that be? What's wrong in the code?
My service class:
#Stateless
public class ReportService {
#PersistenceContext(unitName = "AbcPU") // default type is PersistenceContextType.TRANSACTION
EntityManager em;
public void saveDog() {
System.out.println("BBBBB Start ReportService.saveDog();");
Doggy doggy = new Doggy();
doggy.setDogName("Wuffi");
try {
System.out.println("BBBBB Persist Success ReportService.saveDog();");
em.getTransaction().begin();
em.persist(doggy);
em.flush();
em.getTransaction().commit();
} catch (Exception ex) {
System.out.println("BBBBB Persist Fail ReportService.saveDog();");
System.err.println("Error with em.persist(doggy): " + ex.getMessage());
}
System.out.println("BBBBB Stop ReportService.saveDog();");
}
}
My resource class:
#Path("report")
#Produces(MediaType.APPLICATION_JSON)
public class ReportResource {
#EJB
private ReportService rs;
#GET
public Response findReports() {
final List<Report> reports = rs.findAllReports();
System.out.println("AAAAA Start rs.saveDog();");
rs.saveDog();
System.out.println("AAAAA Stop rs.saveDog();");
return Response.ok(new GenericEntity<List<Report>>(reports) {})
.build();
}
}
My entity class:
#Entity
public class Doggy implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotNull
private String dogName;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getDogName() {
return dogName;
}
public void setDogName(String dogName) {
this.dogName = dogName;
}
#Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof Doggy)) {
return false;
}
Doggy other = (Doggy) object;
if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
#Override
public String toString() {
return "com.glasses.pandora.domain.Doggy[ id=" + id + " ]";
}
}
My persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="AbcPU" transaction-type="JTA">
<jta-data-source>java:/jdbc/abc</jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="javax.persistence.schema-generation.database.action" value="drop-and-create"/>
</properties>
</persistence-unit>
</persistence>
My Maven dependencies in the pom.xml:
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.32</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>4.3.1.Final</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>unknown.binary</groupId>
<artifactId>hibernate-jpamodelgen-4.3.1.Final</artifactId>
<version>SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
My console output after having run:
01:58:54,158 INFO [stdout] (default task-23) AAAAA Start rs.saveDog();
01:58:54,160 INFO [stdout] (default task-23) BBBBB Start ReportService.saveDog();
01:58:54,160 INFO [stdout] (default task-23) BBBBB Persist Success ReportService.saveDog();
01:58:54,160 INFO [stdout] (default task-23) BBBBB Persist Fail ReportService.saveDog();
01:58:54,161 ERROR [stderr] (default task-23) Error with em.persist(doggy): A JTA EntityManager cannot use getTransaction()
01:58:54,161 INFO [stdout] (default task-23) BBBBB Stop ReportService.saveDog();
01:58:54,162 INFO [stdout] (default task-23) AAAAA Stop rs.saveDog();
The mysql show tables:
mysql> show tables;
+--------------------+
| Tables_in_abc |
+--------------------+
| Doggy |
| hibernate_sequence |
+--------------------+
2 rows in set (0.00 sec)
mysql> SELECT * FROM Doggy;
Empty set (0.00 sec)
mysql>
There are a few problems. The first, mentioned by hwellmann, is that you have not specified a datasource. JTA requires a so that connections associated to the JTA transaction are used.
The second is that you don't seem to have wrapped the saveDog method in a transaction. The persist call will only register the Entity with the context; it only gets inserted in the database when the associated transaction is committed, or EntityManager.flush() is called. If you call em.flush in your save method, this will validate that your EntityManager is correctly associated to a transaction and that the insert is successful.
Check the server logs to see if your application is indeed using a MySQL datasource or the embedded default H2 datasource.
Your persistence.xml does not contain a <jta-data-source>, so I'd say the default datasource is being used.
See the Wiki article about Datasource Configuration.
Look at the error - Error with em.persist(doggy): A JTA EntityManager cannot use getTransaction(). EJB is handling transactions automatically - remove the following from your ReportService em.getTransaction().begin() , em.flush(); and em.getTransaction().commit();
Your method should be like this:
public void saveDog() {
System.out.println("BBBBB Start ReportService.saveDog();");
Doggy doggy = new Doggy();
doggy.setDogName("Wuffi");
try {
System.out.println("BBBBB Persist Success ReportService.saveDog();");
em.persist(doggy);
} catch (Exception ex) {
System.out.println("BBBBB Persist Fail ReportService.saveDog();");
System.err.println("Error with em.persist(doggy): " + ex.getMessage());
}
System.out.println("BBBBB Stop ReportService.saveDog();");
}
I am pretty sure you need to look up your persistence context and assign the entitymanager from that.
#Dependent
#Stateful
public class ReportService {
#PersistenceContext(name = "persistence/AbcPU", unitName = "AbcPU")
public abstract EntityManager getEntityManager() {
EntityManager em = null;
try {
Context envCtx = InitialContext.doLookup("java:comp/env");
em = (EntityManager) envCtx.lookup("persistence/AbcPU");
} catch (NamingException ne) {
//Handle Exception
}
return em;
}
public void saveDog() {
System.out.println("BBBBB Start ReportService.saveDog();");
Doggy doggy = new Doggy();
doggy.setDogName("Wuffi");
try {
System.out.println("BBBBB Persist Success ReportService.saveDog();");
getEntityManager().persist(doggy);
} catch (Exception ex) {
System.out.println("BBBBB Persist Fail ReportService.saveDog();");
System.err.println("Error with em.persist(doggy): " + ex.getMessage());
}
System.out.println("BBBBB Stop ReportService.saveDog();");
}
}
Related
I'm trying to do an update in my local database, but when I excute an "executeUpdate" I'm getting the next error :
javax.persistence.TransactionRequiredException: Exception
Description: No externally managed transaction is currently active for
this thread
This is my persistence.xml :
<?xml version="1.0" encoding="windows-1252" ?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
<persistence-unit name="GestionPagoTramiteUtilImplementacion">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/servicioswebDS</jta-data-source>
<class>modelo.Transferencias</class>
<properties>
<property name="eclipselink.target-server" value="WebLogic_10"/>
<property name="javax.persistence.jtaDataSource" value="jdbc/servicioswebDS"/>
</properties>
</persistence-unit>
</persistence>
This is the Class Transferencias.java :
#Entity
#NamedQueries({
#NamedQuery(name = "Transferencias.findAll", query = "select o from Transferencias o"),
#NamedQuery(name = "Transferencias.asociarPagoTramiteUpdate", query = "UPDATE Transferencias o SET o.usado = :p_usado , o.cdgpraradicacion = :p_codigoParaRadicacion WHERE o.trazabilitycode = :p_identificadorTransaccion")
})
public class Transferencias implements Serializable {
}
I have a SessionBean when I'm calling the class Transferencias Using an EntityManager
#Stateless(name = "PagoTramiteSession",
mappedName = "InvimaPortafolioServicios-GestionPagoTramiteUtilImplementacion-PagoTramiteSession")
public class PagoTramiteSessionBean implements PagoTramiteSession, PagoTramiteSessionLocal {
#Resource
SessionContext sessionContext;
#PersistenceContext(unitName = "GestionPagoTramiteUtilImplementacion")
private EntityManager em;
public PagoTramiteSessionBean() {
}
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public boolean actualizarPagoTramite(BigDecimal codigoParaRadicacion, String identificadorTransaccion,
String usado) throws SQLException {
try {
Query query;
// String sql =
// "UPDATE Transferencias t " + " SET t.usado = :p_usado, " +
// "t.cdgpraradicacion = :p_codigoParaRadicacion " +
// " WHERE t.trazabilitycode = :p_identificadorTransaccion";
// query = em.createQuery(sql);
// query.setParameter("p_usado", usado);
// query.setParameter("p_codigoParaRadicacion", codigoParaRadicacion);
// query.setParameter("p_identificadorTransaccion", identificadorTransaccion);
// int retorno = query.executeUpdate();
// if (retorno >= 1) {
// return true;
// } else {
// return false;
// }
query = em.createNamedQuery("Transferencias.asociarPagoTramiteUpdate");
query.setParameter("p_usado", usado);
query.setParameter("p_codigoParaRadicacion", codigoParaRadicacion);
query.setParameter("p_identificadorTransaccion", identificadorTransaccion);
int retorno = query.executeUpdate();
if (retorno >= 1) {
return true;
} else {
return false;
}
} catch (Exception e) {
System.out.println(e);
}
return false;
}
}
I have tried to execute the update in two ways, using a nativeQuery and using a createQuery method by entityManager.
When I execute a em.getResultList() this works fine, but when I'm trying to do an update I have the error.
You have not specified a transaction manager
You need something like
<persistence-unit name="GestionPagoTramiteUtilImplementacion" transaction-type="RESOURCE_LOCAL">>
See http://www.eclipse.org/eclipselink/documentation/2.5/solutions/testingjpa002.htm
I use Kundera-Cassandra 3.2 and want to use the transaction management from Kundera.
My handling looks like this:
EntityManager manager = repo.getEntityManagerFactory().createEntityManager(CassandraRepository.getProperties());
try{
manager.getTransaction().begin();
this.repo.update(account1, manager); //calls the merge method of the Entitymanager
this.repo.save(account2, manager); //calls the persist method of the Entitymanager
manager.getTransaction().commit();
} catch(Exception e){
if(manager.getTransaction().isActive()){
manager.getTransaction().rollback();
}
} finally {
manager.clear();
manager.close();
}
When an error in the this.repo.save(account2, manager); occurs, the manager rollbacks the transaction, but does not do a update statement, he makes a delete statement for the merge method. The reason for this is, when calling the merge methode, kundera creates an insert statement and not an update. But how to say Kundera to make an update to rollback the transaction also with an update.
Logs:
12:42:41.185 [http-bio-8080-exec-3] INFO com.impetus.client.cassandra.CassandraClientBase - Returning delete query DELETE FROM "account" WHERE "id" = 'MCSP-000000000004'.
12:42:41.211 [http-bio-8080-exec-3] INFO com.impetus.client.cassandra.CassandraClientBase - Returning delete query DELETE FROM "account" WHERE "id" = 'MCSP-000000000005'.
EDIT (my repository):
public class CassandraRepository<T> {
#PersistenceUnit
private EntityManagerFactory entityManagerFactory;
public static Map<String, String> getProperties() {
final Map<String, String> properties = new HashMap<String, String>();
properties.put(CassandraConstants.CQL_VERSION, CassandraConstants.CQL_VERSION_3_0);
return properties;
}
public void update(T entity, EntityManager manager) throws Exception{
try {
manager.merge(entity);
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
public void save(T entity, EntityManager manager) throws Exception{
try {
manager.persist(entity);
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
}
According to JPA, to update an entity you have to first bring it into managed state (by fetching it)
Example:-
PersonCassandra p = entityManager.find(PersonCassandra.class, "2");
entityManager.getTransaction().begin();
p.setMonth(Month.JAN);
entityManager.merge(p);
entityManager.persist(p3);
entityManager.getTransaction().commit();
Issue is not with INSERT and UPDATE statements since both are similar for Cassandra, under the hood.
Can someone please tell me why am I geting java.sql.SQLException: This function is not supported using HSQL and Spring? I am trying to insert a new row into my database..
Below is my DAO and I get the error on the mySession.save(message) line:
#Transactional
#Repository
public class MessageDaoImpl implements MessageDao
{
private Log log = null;
#Autowired
private SessionFactory sessionFactory;
public MessageDaoImpl()
{
super();
log = LogFactory.getLog(MessageDaoImpl.class);
}
#SuppressWarnings("unchecked")
#Transactional(readOnly = true, propagation = Propagation.REQUIRED)
public List<Message> listMessages()
{
try
{
return (List<Message>) sessionFactory.getCurrentSession()
.createCriteria(Message.class).list();
} catch (Exception e)
{
log.fatal(e.getMessage());
return null;
}
}
#SuppressWarnings("unchecked")
#Transactional(readOnly = false, propagation = Propagation.REQUIRED)
public void SaveOrUpdateMessage(Message message)
{
try
{
Session mySession = sessionFactory.getCurrentSession();
mySession.save(message);
mySession.flush();
} catch (Exception e)
{
log.fatal(e.getMessage());
}
}
}
Here is my main class:
public static void main(String[] args)
{
ApplicationContext context = new AnnotationConfigApplicationContext(HelloWorldConfig.class);
MessageService mService = context.getBean(MessageService.class);
HelloWorld helloWorld = context.getBean(HelloWorld.class);
/**
* Date: 4/26/13 / 9:26 AM
* Comments:
*
* I added Log4J to the example.
*/
LOGGER.debug("Message from HelloWorld Bean: " + helloWorld.getMessage());
Message message = new Message();
message.setMessage(helloWorld.getMessage());
//
mService.SaveMessage(message);
helloWorld.setMessage("I am in Staten Island, New York");
LOGGER.debug("Message from HelloWorld Bean: " + helloWorld.getMessage());
}
}
Here is my DatabaseConfig:
public class DatabaseConfig
{
private static final Logger LOGGER = getLogger(DatabaseConfig.class);
#Autowired
Environment env;
#Bean
public DataSource dataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
EmbeddedDatabase db = builder.setType(EmbeddedDatabaseType.HSQL).
addScript("schema.sql").build();
return db;
}
#Bean
public DataSource hsqlDataSource() {
BasicDataSource ds = new BasicDataSource();
try {
ds.setDriverClassName("org.hsqldb.jdbcDriver");
ds.setUsername("sa");
ds.setPassword("");
ds.setUrl("jdbc:hsqldb:mem:mydb");
}
catch (Exception e)
{
LOGGER.error(e.getMessage());
}
return ds;
}
#Bean
public SessionFactory sessionFactory()
{
LocalSessionFactoryBean factoryBean = new LocalSessionFactoryBean();
factoryBean.setDataSource(hsqlDataSource());
factoryBean.setHibernateProperties(getHibernateProperties());
factoryBean.setPackagesToScan(new String[]{"com.xxxxx.HelloSpringJavaBasedJavaConfig.model"});
try
{
factoryBean.afterPropertiesSet();
} catch (IOException e)
{
LOGGER.error(e.getMessage());
e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
return factoryBean.getObject();
}
#Bean
public Properties getHibernateProperties()
{
Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect"));
hibernateProperties.setProperty("hibernate.show_sql", env.getProperty("hibernate.show_sql"));
hibernateProperties.setProperty("hibernate.use_sql_comments", env.getProperty("hibernate.use_sql_comments"));
hibernateProperties.setProperty("hibernate.format_sql", env.getProperty("hibernate.format_sql"));
hibernateProperties.setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
hibernateProperties.setProperty("hibernate.generate_statistics", env.getProperty("hibernate.generate_statistics"));
hibernateProperties.setProperty("javax.persistence.validation.mode", env.getProperty("javax.persistence.validation.mode"));
//Audit History flags
hibernateProperties.setProperty("org.hibernate.envers.store_data_at_delete", env.getProperty("org.hibernate.envers.store_data_at_delete"));
hibernateProperties.setProperty("org.hibernate.envers.global_with_modified_flag", env.getProperty("org.hibernate.envers.global_with_modified_flag"));
return hibernateProperties;
}
#Bean
public HibernateTransactionManager hibernateTransactionManager()
{
HibernateTransactionManager htm = new HibernateTransactionManager();
htm.setSessionFactory(sessionFactory());
htm.afterPropertiesSet();
return htm;
}
}
and here is what I am getting to the console:
Exception in thread "main" org.hibernate.AssertionFailure: null id in com.xxx.HelloSpringJavaBasedJavaConfig.model.Message entry (don't flush the Session after an exception occurs)
Here is my message model bean:
#Entity
#Table(name = "messages")
public class Message
{
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private String id;
#Column(name = "message")
private String message;
public String getId()
{
return id;
}
public void setId(String id)
{
this.id = id;
}
public String getMessage()
{
return message;
}
public void setMessage(String message)
{
this.message = message;
}
#Override
public String toString()
{
return "Message{" +
"id='" + id + '\'' +
", message='" + message + '\'' +
'}';
}
}
This relates to the version of hsql being used probably the version causing issue was 1.8 with hibernate 4. Need to use 2.2.9 instead
You can't use a String with #GenerateValue with the Strategy GenerationType.AUTO since it uses sequence generator and those can't be used with non-numerical values. You have to set it yourself. Use an Integer or Long if you want it to be generated for you.
Hibernate docs
Or use an id generator that uses string values
#Id
#GeneratedValue(generator="system-uuid")
#GenericGenerator(name="system-uuid", strategy = "uuid")
it was a version issues. I updated the versions and now everything works
I had the same issue after I upgraded hibernate to version 4.2.8 .Looking in the logs, I noticed that the sql query generated by hibernate tried to insert a record with a null primary key. The field was annotated just with: #Id #GeneratedValue
Upgrading hsqldb to version 2.2.9 made this disappear just like Denis said and I am very thankful to him for the reply.
It seems very likely that this issue is related to attempting to use a Session which has already signaled an error. As Sotirios mentioned, it is a bad idea to catch exceptions in your DAO, and if you do, it is critical that you re-throw them.
Normally, once you catch a Hibernate exception, you must roll back your transaction and close the session as the session state may no longer be valid (Hibernate core documentation).
If the Session throws an exception, including any SQLException, immediately rollback the database transaction, call Session.close() and discard the Session instance. Certain methods of Session will not leave the session in a consistent state. No exception thrown by Hibernate can be treated as recoverable. Ensure that the Session will be closed by calling close() in a finally block.
Since you're using Spring, most of that doesn't apply to you, but the exception message you are seeing indicates that the actual cause of your problem probably was related to catching an exception and then continuing to use the same session.
I am using Postgresql, Hibernate and JPA. Whenever there is an exception in the database, I get something like this which is not very helpful as it does not show what really went wrong on the DB server.
Caused by: java.sql.BatchUpdateException: Batch entry 0 update foo set ALERT_FLAG='3' was aborted. Call getNextException to see the cause.
at org.postgresql.jdbc2.AbstractJdbc2Statement$BatchResultHandler.handleError(AbstractJdbc2Statement.java:2621)
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1837)
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:407)
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeBatch(AbstractJdbc2Statement.java:2754)
at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeBatch(NewProxyPreparedStatement.java:1723)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:70)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:268)
... 82 more
I want the exception message from the database to appear in the application's log.
I came across this article which uses an Aspect to populate the exception chain which is otherwise not populated properly in case of SQLExceptions.
Is there a way to fix this without using Aspects or any custom code. Ideal solution would involve only config file changes.
This worked for me to get the exception message which caused the problem (Hibernate 3.2.5.ga):
catch (JDBCException jdbce) {
jdbce.getSQLException().getNextException().printStackTrace();
}
There is no need to write any custom code to achieve this - Hibernate will log the exception cause by default. If you can't see this, Hibernate logging must not be set up correctly. Here's an example with slf4j+log4j, and using Maven for dependency management.
src/main/java/pgextest/PGExceptionTest.java
public class PGExceptionTest {
public static void main(String[] args) throws Exception {
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory(
"pgextest");
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
// here I attempt to persist an object with an ID that is already in use
entityManager.persist(new PGExceptionTestBean(1));
entityManager.getTransaction().commit();
entityManager.close();
}
}
src/main/resources/log4j.properties
log4j.rootLogger=ERROR, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
src/main/resources/META-INF/persistence.xml
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
<persistence-unit name="pgextest">
<properties>
<property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:postgresql://localhost/pgextest"/>
<property name="javax.persistence.jdbc.user" value="postgres"/>
<property name="javax.persistence.jdbc.password" value="postgres"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
<property name="hibernate.jdbc.batch_size" value="5"/>
</properties>
</persistence-unit>
</persistence>
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>pgextest</groupId>
<artifactId>pgextest</artifactId>
<version>0.0.1-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>3.6.9.Final</version>
</dependency>
<dependency>
<groupId>postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.1-901.jdbc4</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.1</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.15</version>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>
Executing the main method will then log the following:
ERROR [main] - Batch entry 0 insert into PGExceptionTestBean (label, id) values (NULL, '1') was aborted. Call getNextException to see the cause.
ERROR [main] - ERROR: duplicate key value violates unique constraint "pgexceptiontestbean_pkey"
It's probably worth mentioning that you can disable the JDBC batching that wraps the original exception by setting the property hibernate.jdbc.batch_size to 0 (needless to say you probably don't want to do this in production.)
I think Aspect Programming is a better solution to solve this kind of problem.
But, if you want to write a custom code to do that, you can catch SqlException and loop through it and log each exception. Something like this should work.
try {
// whatever your code is
} catch (SQLException e) {
while(e!= null) {
logger.log(e);
e = e.getNextException();
}
}
For me the exception was a PersistenceException, so I had to do this:
try {
//...
} catch (javax.persistence.PersistenceException e) {
log.error(((java.sql.BatchUpdateException) e.getCause().getCause()).getNextException());
}
try {
// code
} catch (SQLException e) {
for (Throwable throwable : e) {
log.error("{}", throwable);
}
}
Just in case if it so happens that you are getting this exception from inside a JUnit test you can transform the JUnit exception with a TestRule (this is was inspired by the source of ExpectedException TestRule)
public class HibernateBatchUnwindRule implements TestRule {
private boolean handleAssumptionViolatedExceptions = false;
private boolean handleAssertionErrors = false;
private HibernateBatchUnwindRule() {
}
public static HibernateBatchUnwindRule create(){
return new HibernateBatchUnwindRule();
}
public HibernateBatchUnwindRule handleAssertionErrors() {
handleAssertionErrors = true;
return this;
}
public HibernateBatchUnwindRule handleAssumptionViolatedExceptions() {
handleAssumptionViolatedExceptions = true;
return this;
}
public Statement apply(Statement base,
org.junit.runner.Description description) {
return new ExpectedExceptionStatement(base);
}
private class ExpectedExceptionStatement extends Statement {
private final Statement fNext;
public ExpectedExceptionStatement(Statement base) {
fNext = base;
}
#Override
public void evaluate() throws Throwable {
try {
fNext.evaluate();
} catch (AssumptionViolatedException e) {
optionallyHandleException(e, handleAssumptionViolatedExceptions);
} catch (AssertionError e) {
optionallyHandleException(e, handleAssertionErrors);
} catch (Throwable e) {
handleException(e);
}
}
}
private void optionallyHandleException(Throwable e, boolean handleException)
throws Throwable {
if (handleException) {
handleException(e);
} else {
throw e;
}
}
private void handleException(Throwable e) throws Throwable {
Throwable cause = e.getCause();
while (cause != null) {
if (cause instanceof BatchUpdateException) {
BatchUpdateException batchUpdateException = (BatchUpdateException) cause;
throw batchUpdateException.getNextException();
}
cause = cause.getCause();
};
throw e;
}
}
and then add the rule to the test case
public class SomeTest {
#Rule
public HibernateBatchUnwindRule batchUnwindRule = HibernateBatchUnwindRule.create();
#Test
public void testSomething(){...}
}
If by some chance you are encountering this exception from Kafka-Connect, you can set the batch.size property to 0 (temporarily) to reveal the exception encountered by your sink worker.
In my case, when I was using java and postgreSQL database I got this exception. After checking the record which I tried to insert to the table, it was found the record had duplicate id and it violated unique constraints. Therefore it is better to check record that you are going to insert and try to insert it using db client to see extract error.
Now I am using jpa with hibernate , when i was done getEntityManager.persist(objects) then i will ask for user confirmation like continue and rollback using user interface
private List<TempCustomers> tempCustomer =new ArrayList<TempCustomers>();
#Begin(join = true)
public String migrateData() {
log.info("Mobee Migrate Customer Size :"+doTempCustomers.size());
for(DoTempCustomers tempCustomers:doTempCustomers){
try {
TempCustomers temp=new TempCustomers();
BeanUtils.copyProperties(temp, tempCustomers);
tempCustomer.add(temp);
getEntityManager().persist(temp);
}catch (Exception e) {
// TODO: handle exception
return "null";
}
}
log.info("Size........."+tempCustomer.size());
return "null";
}
#Begin(join = true)
public String updatedData(){
log.info("Size of Customers :"+tempCustomer.size());
log.info("Decision ..."+decision);
try{
if(decision.equals("Continue")){
for(TempCustomers tempCust:tempCustomer){
TempCustomers temp=new TempCustomers();
BeanUtils.copyProperties(temp, tempCust);
log.info("updated Sucessfully");
getEntityManager().getTransaction().commit();
}}else{
getEntityManager().getTransaction().rollback();
}
}
catch(Exception e){
}
}
please help me how to do continue and rollback in jpa with hibernate when getEntityManager().persist() is done.
To commit with JPA:
entityManager.getTransaction().commit();
To rollback with JPA:
entityManager.getTransaction().rollback();
Call either of these methods after your call to persist to perform the desired action. In your case entityManager would be replaced with the call to retrieve the entityManager, getEntityManager()
Reference: http://www.objectdb.com/java/jpa/persistence/store