I'm new to hibernate and I've written the following code,
package main;
import java.util.List;
import model.Message;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.stat.Statistics;
public class Main {
public static void main(String[] args) throws Exception {
Configuration configuration = new Configuration().configure();
SessionFactory factory = configuration.buildSessionFactory();
Session session = factory.openSession();
//Transaction transaction = session.beginTransaction();
List<Message> messages = session.createQuery("from Message").list();
Message message = new Message();
message.setMessage("Hello World");
session.save(message);
System.out.println(messages);
System.out.println(session);
session.close();
factory.close();
System.out.println("After the closure");
}
}
Here I've not begun the transaction and I've closed the session. But Hibernate didn't complain about Transactions. Are there implicit transactions, if so how should I disable them.
Hibernate didn't had a reason to complain about Transactions. :)
You closed the session after you did the work. You shouldn't create a new sessionFactory for each database edit though:
A SessionFactory is an expensive-to-create, threadsafe object,
intended to be shared by all application threads. It is created once,
usually on application startup, from a Configuration instance.
A Session is an inexpensive, non-threadsafe object that should be used
once and then discarded for: a single request, a conversation or a
single unit of work. A Session will not obtain a JDBC Connection, or a
Datasource, unless it is needed. It will not consume any resources
until used.
Read more about Hibernate transaction management here.
Related
I have the following error and I am trying to figure out the reason. As far as I know, this is not caused by Hibernate but the way I manage/use the method calls (Transient entity instance, save, detach and then merge). I do not get why this problem happens. I would really appreciate your explanation.
Error:
HHH000099: an assertion failure occurred (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session): org.hibernate.AssertionFailure: possible non-threadsafe access to session
FYI:
Java Version: 1.8
Hibernate version: https://hibernate.org/orm/releases/5.5/
package lk.mysite.demo.orm;
import lk.mysite.demo.orm.entity.Customer;
import lk.mysite.demo.orm.util.HibernateUtil;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
public class ContextDemo16 {
public static void main(String[] args) {
try (SessionFactory sf = HibernateUtil.getSessionFactory();
Session session = sf.openSession();) {
session.beginTransaction();
Customer customer = new Customer(5, "John", "New York");
session.save(customer);
session.detach(customer);
session.merge(customer);
// prompt an error
session.getTransaction().commit();
} catch (HibernateException e) {
e.printStackTrace();
}
}
}
I tried to configure Spring Boot with Hibernate:
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import org.datalis.plugin.database.dao.TerminalsService;
import org.datalis.plugin.database.models.TerminalsModel;
import org.hibernate.Session;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
#Service
#Qualifier("terminalsService")
public class TerminalsDaoHibernate implements TerminalsService {
#Autowired
private EntityManager entityManager;
#Override
#Transactional
public TerminalsModel getTerminalToken(String terminalToken) throws Exception {
TerminalsModel terminal = null;
Session session = entityManager.unwrap(Session.class);
try {
entityManager.getTransaction().begin();
terminal = (TerminalsModel) session.get(TerminalsModel.class, terminalToken);
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
throw new Exception("Error");
}
return terminal;
}
}
But I get this error:
14:47:34,323 ERROR [stderr] (default task-1) java.lang.IllegalStateException: Not allowed to create transaction on shared EntityManager - use Spring transactions or EJB CMT instead
14:47:34,323 ERROR [stderr] (default task-1) at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:255)
What is the proper way to configure #Transactional properly?
Do I need to use Transaction is a different way?
You are using #Transactional and still are trying to manually start a transaction. Either do manual transaction management (i.e remove the #Transactional) or embrace #Transactional by removing the manual transaction management code.
#Override
#Transactional
public TerminalsModel getTerminalToken(String terminalToken) throws Exception {
TerminalsModel terminal = null;
Session session = entityManager.unwrap(Session.class);
return (TerminalsModel) session.get(TerminalsModel.class, terminalToken);
}
However I don't see why you would want to use plain Hibernate over JPA here. The same result can be achieved by using JPA.
#Override
#Transactional
public TerminalsModel getTerminalToken(String terminalToken) throws Exception {
return entityManager.find(TerminalsModel.class, terminalToken);
}
Generally there is no need to use the plain Hibernate API over JPA with the current state of the JPA API.
Im trying to develop a "Message Driven Bean" to handle all the local ActiveMQ messages, but it's the first time that i try to do something like this.
The most part of the material that i found explain how to write a MDB using JBOSS server, in this case there's a xml file with some queue information, but in all wildfly tutorials there's no mention to any kind of configuration like that.
I have the following scenario:
A simple java project like message producer
An ActiveMQ instance running local
An EJB project deployed into Wildfly 10
My producer project is able to send messages to ActiveMQ queue, this part its working,but my EJB project just have a single class called TestMDBHandle with #MessageDriven annotation. Is this enough to receive my queue messages? Because the MDB isnt working, i imagine must be a kind of configuration or property in EJB to specify the host of the message-broker.
My message producer:
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class MessageSender {
public static void main(String args[]) throws NamingException, JMSException {
MessageSender sender = new MessageSender();
sender.sender();
}
public void sender() throws NamingException, JMSException {
InitialContext jndi = null;
Session session = null;
Connection connection = null;
try {
jndi = new InitialContext();
ConnectionFactory factory = (ConnectionFactory)jndi.lookup("connectionFactory");
connection = factory.createConnection();
connection.start();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination destination = (Destination)jndi.lookup("MyQueue");
MessageProducer producer = session.createProducer(destination);
TextMessage mensagem = session.createTextMessage("Eu enviei uma mensagem!");
producer.send(mensagem);
} catch (Exception e) {
e.printStackTrace();
} finally {
session.close();
connection.close();
jndi.close();
}
}
}
My jms properties located inside my producer project
java.naming.factory.initial=org.apache.activemq.jndi.ActiveMQInitialContextFactory
java.naming.provider.url=tcp://localhost:61616
connectionFactoryNames = connectionFactory, queueConnectionFactory, topicConnectionFactory
queue.MyQueue=jms/myqueue
Finally, my ejb project have this single class, without any kind of property file or xml.
package br.com.jms.mdb;
import javax.annotation.Resource;
import javax.ejb.ActivationConfigProperty;
import javax.ejb.EJB;
import javax.ejb.MessageDriven;
import javax.ejb.MessageDrivenContext;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
#MessageDriven(name = "meuHandler", activationConfig = {
#ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
#ActivationConfigProperty(propertyName = "destination", propertyValue = "jms/myqueue") })
public class Teste implements MessageListener {
#Resource
private MessageDrivenContext mdctx;
public Teste() {
}
#Override
public void onMessage(Message message) {
TextMessage objectMessage = null;
try {
objectMessage = (TextMessage)message;
System.out.println("Achei a mensagem : " + objectMessage.getText().toString());
}catch(JMSException e) {
e.printStackTrace();
}
}
}
Maybe you can provide a little more information such as the xml file with the queue information and the annotation properties of the MDB? Because it sounds you are heading in the right direction. The two main things:
You have to specify the exact queue that the MDB is listening to, for example through the properties of the #MessageDriven annotation (such as "name", "mappedName", "activationConfig"). And of course override the onMessage() method to process the messages.
You also have to make sure that this specific queue is available as a resource for your application. You have to provide jms configuration for this, which also defines the resource type (Queue or Topic). From your question I can't tell which of these steps you have (partly) completed.
I am getting the title error while compiling my java file. But I am not getting where is my mistake..Can anyone please help me? Below is my code:
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
//imported UserDetailInfo class also here..
public class UserInfo {
public static void main(String[] args){
Session session=null;
try{
UserDetailInfo demopojo=new UserDetailInfo();
Configuration configuration = new Configuration();
SessionFactory sessionFactory = configuration.configure().buildSessionFactory();
session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();
transaction.begin();
demopojo.setId(1);
demopojo.setFirstName("Johny");
demopojo.setLastName("John");
session.save(demopojo);
transaction.commit();
}catch(Exception e){
System.out.println(e.getMessage());
}finally{
session.close();
}
}
}
i had the same problem try to remove
#SuppressWarnings("serial")
if you have it above main class. it worked for me.
I have a stateless session EJB as per 3.0 spec.
/*Remote Interface*/
package com.nseit.ncfm2.data.ejb;
import java.sql.SQLException;
import java.util.Collection;
import javax.ejb.Remote;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.naming.NamingException;
import com.nseit.ncfm2.security.Audit;
#Remote
public interface ProductionDataChangesRequestsRemote {
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public boolean shiftCandidateDetails(String sourceNcfmId,
String destinationNcfmId, Collection<String> specialCasesList, String shiftingRemarks, String user, Audit updtAudit) throws NamingException, SQLException;
}
/*Bean Class*/
package com.nseit.ncfm2.data.ejb;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.naming.NamingException;
import com.nseit.ncfm2.security.Audit;
import com.nseit.ncfm2.util.server.lookup.LookUpServerResources;
import java.sql.*;
import java.util.*;
/**
* Session Bean implementation class ProductionDataChangesRequestsBean
*/
#Stateless(name = "ProductionDataChangesRequestsBean", mappedName = "ProductionDataChangesRequestsEJB")
#Remote(ProductionDataChangesRequestsRemote.class)
#TransactionManagement(TransactionManagementType.CONTAINER)
public class ProductionDataChangesRequestsBean implements
ProductionDataChangesRequestsRemote {
/**
* Default constructor.
*/
public ProductionDataChangesRequestsBean() {
// TODO Auto-generated constructor stub
}
#Override
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public boolean shiftCandidateDetails(String sourceNcfmId,
String destinationNcfmId, Collection<String> specialCasesList,
String shiftingRemarks, String user, Audit updtAudit)
throws NamingException, SQLException {
// TODO Auto-generated method stub
Connection conn = null;
PreparedStatement pstmt = null;
int updtCnt = 0;
boolean areDetailsShifted = false;
try {
..............
..............
..............
/* Start: update table-1 */
..............
..............
..............
updtCnt = pstmt.executeUpdate();
..............
..............
..............
/* End: update table-1 */
/* Start: update table-2 */
..............
..............
..............
updtCnt = pstmt.executeUpdate();
..............
..............
..............
/* End: update table-2 */
areDetailsShifted = true;
} /*catch (SQLException e) {
// TODO Auto-generated catch block
System.out
.println("SQLException in ProductionDataChangesRequestsBean.shiftCandidateDetails(...) "
+ e.getMessage());
// e.printStackTrace();
context.setRollbackOnly();
} */finally {
LookUpServerResources.closeStatement(pstmt);
LookUpServerResources.closeConnection(conn);
}
return areDetailsShifted;
}
}
Currently, if the 1st table update succeeds and the 2nd table update gives an exception, a rollback is not taking place, i.e records in 1st table are updated.
I want the transaction to be rolled back in case an SQLException occurs (or for that matter, if any runtime exception occurs).
I tried two approaches :
Use of context.setRollbackOnly() in catch block for SQLException
Throwing the SQLException
In both the cases, the transaction didn't roll back.
How can I achieve this:
Without the usage of #ApplicationException annotation (as I do not have any application exceptions)
Without catching the SQLException and then calling context.setRollbackOnly()
Or what is the standard way?
You will have to throw RuntimeException
The standard way is to use underlying JPA for persistence rather then using JDBC.
JPA provides a standard OR mapping solution that's well-integrated into an EJB 3.x-compliant container.
Also from your code it reflects that you have TransactionManagementType.CONTAINER , but still managing transaction manually.
In bean-managed transaction
demarcation, the code in the session
or message-driven bean explicitly
marks the boundaries of the
transaction. Although beans with
container-managed transactions require
less coding, they have one limitation:
When a method is executing, it can be
associated with either a single
transaction or no transaction at all.
If this limitation will make coding
your bean difficult, you should
consider using bean-managed
transactions.
It looks like you are using JDBC API in your bean. I don't think container manages those JDBC transactions. For CMT, you'd have to invoke operations on a container managed entity manager for the rollback to work as expected.