i read https://www.baeldung.com/spring-open-session-in-view article
According to this article, it says that the session is created in the request phase.
Spring opens a new Hibernate Session at the beginning of the request. These Sessions are not necessarily connected to the database.
But as far as I know, in Spring, when a transaction starts, it gets an entity manager (session). The reason is that there is no need to create an entity manager for logic that does not use JDBC.
#Transactional
#Service
public class DoService {
// when use this service EntityManager is created;
}
I wonder if I'm getting it wrong.
Related
I am having trouble finding information about this issue I am running into. I am interested in implementing row level security on my Postgres db and I am looking for a way to be able to set postgres session variables automatically through some form of an interceptor. Now, I know that with hibernate you are able to do row-level-security using #Filter and #FilterDef, however I would like to additionally set policies on my DB.
A very simple way of doing this would be to execute the SQL statement SET variable=value prior to every query, though I have not been able to find any information on this.
This is being used on a spring-boot application and every request is expected to will have access to a request-specific value of the variable.
Since your application uses spring, you could try accomplishing this in one of a few ways:
Spring AOP
In this approach, you write an advice that you ask spring to apply to specific methods. If your methods use the #Transactional annotation, you could have the advice be applied to those immediately after the transaction has started.
Extended TransactionManager Implementation
Lets assume your transaction is using JpaTransactionManager.
public class SecurityPolicyInjectingJpaTransactionManager extends JpaTransactionManager {
#Autowired
private EntityManager entityManager;
// constructors
#Override
protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
super.prepareSynchronization(status, definition);
if (status.isNewTransaction()) {
// Use entityManager to execute your database policy param/values
// I would suggest you also register an after-completion callback synchronization
// This after-completion would clear all the policy param/values
// regardless of whether the transaction succeeded or failed
// since this happens just before it gets returned to the connection pool
}
}
}
Now simply configure your JPA environment to use your custom JpaTransactionManager class.
There are likely others, but these are the two that come to mind that I've explored.
I was working on a project using Spring boot, Spring MVC, and Hibernate. I encountered this problem which had already taken me 2 days.
My project was an imitation of twitter. When I started to work on the project, I used the JPA to get the Hibernate Session. Here is the code in my BaseDaoImpl class:
#Autowired
private EntityManagerFactory entityManagerFactory;
public Session getSession(){
return entityManagerFactory.createEntityManager().unwrap(Session.class);
}
In my Service class, I used the #Transactional annotation:
#Service("userServ")
#Transactional(propagation=Propagation.REQUIRED, readOnly=false,rollbackFor={Exception.class, RuntimeException.class})
public class UserServImpl implements IUserServ {}
And finally, an overview of my main class:
#SpringBootApplication
#EnableTransactionManagement
#EntityScan(basePackages = {"edu.miis.Entities"})
#ComponentScan({"edu.miis.Controllers","edu.miis.Service","edu.miis.Dao"})
#EnableAutoConfiguration
#Configuration
public class FinalProjectSpringbootHibernateDruidApplication {
public static void main(String[] args) {
SpringApplication.run(FinalProjectSpringbootHibernateDruidApplication.class, args);
}
}
When I was using this setting, everything seemed fine - until I was able to move up to a the extent where I started to add "post" function. I could add posts and comments into the database. However, I could not do this a lot of times. Every time I added up to 4 posts, the program ceased to run - no exceptions, no errors - the page just got stuck there.
I looked up online, realizing that the problem was probably due to the entityManagerFactory. I was told that entityManagerFactory.createEntityManager().unwrap(Session.class)opens new Hibernate sessions, instead of the traditional sessionFactory.getCurrentSession() that returns an existing session.
So I started to work on it. I changed my Dao configuration into this:
#Autowired
private EntityManagerFactory entityManagerFactory;
public Session getSession(){
Session session = entityManagerFactory.unwrap(SessionFactory.class).getCurrentSession();
return session;
}
My idea was to use the autowired EntityManagerFactory to return a Hibernate SessionFactory so that the getCurrentSession method can be then used.
But then I got problem:
Since I configured to this setting, any operation that involves input from controller to the service-dao-database invokes an exception: No Transaction Is In Progress
But the weird thing is: although the system broke due to no visible transaction in progress, Hibernate still generates new SQL statements, and data still get synchronized into the database.
Can anybody help me over how to get this issue resolved?
Sincerely thanks!
Following #M. Deinum's suggestion, I finally had this issue resolved.
The reason why the #Transactional annotation didn't work in my code in the first place, was because in my original code, I used plain Hibernate features - Session, SessionFactory, getCurrentSession() etc.
In order for these features to work, I need to specifically configure the transaction manager into a Hibernate Transaction Manager (under default setting, Spring boot autowires a JPA transaction manager).
But the problem is: most of methods that were used to support Hibernate features are now deprecated. Now, the JPA method is the mainstream.
Use EntityManager instead of Session/
Use EntityManager.persist instead of Session.save
Use EntityManager.merge instead of Session.update
Use EntityManager.remove instead of Session.remove.
That's all.
Thanks!
I am trying to understand the transactions management and try to use its power in my already existing application developed in Struts 2, EJB 3 and hibernate 5.2.
Now I have ejb in my business layer like below
#Stateless
#TransactionManagement(TransactionManagementType.CONTAINER)
public class MyEJb implements ejbxyz {
#Override
public void method(){
Dao dao=new Dao() //Dao class is simple java class
dao.fooMethod(); //this method updates some record
dao.barMethod(); // this method updates some other record
}
}
public class Dao{
fooMethid(){
Session session=sessFactory.openSession();
session.beginTransaction();
session.update(x);
}
barMethod(){
try{
Session session=sessFactory.getCurrentSession();
session.getNamedQuery("xyz").executeUpdate();
}catch(HibernateException ex){
session.getTransaction.rollback();
}
}
}
I understand that Transaction management should be done at service layer(at ejb in my case). But how can I achieve this over there. ?
Now the dependency is if barMethod() fails to update the record then I need to rollback the changes made in fooMethod. So basically I need both the methods to be done in one transaction.
When I execute the application it throws the below exception
Exception while barMethod getNamedQuery is not valid without active transaction
Its because I am not beginning any transaction in barMethod. But then I really dont want to start a new transaction and want to continue with the older transaction started in fooMethod.
Container managed transactions are indeed suported out of the box for EJB beans. However, your Dao class is not a managed bean - it is a regular pojo that you instantiate manualy - therefore it does not participate in any transaction started by your other ejb.
So move your Dao to separate file, annotate it with #Stateless and then inject it into your service using #EJB private Dao dao;
There is more to transactions in Ejb container though. You can control the transaction support on method level via #TransactionAttribute annotation, that specifies how should the container invoke your method with regard to transaction. That way you can control, whether your method requires its own transaction, or if it shall participate in a transaction initiated by the caller(e.g. when invoked from ejb bean). For more info have a look at official Java EE tutorial
I am writing a small tool for training purposes.
The main scope is to understand how to work with persistent data without using ORM tools.
So, there is a small DB with tables:
users(id, login, password, group_id)
groups(id, name, description)
roles(id, name, description)
// for many-to-many relationship between groups and roles.
groupsroles(group_id, role_id)
So, I have implemented a DAO pattern, with 3 entities : User, Role, Group.
The question is : what is the best way to implement the work with relationships?
How to share the same connection between UserDAO, GroupDAO, RoleDAO to use DB transactions ?
One option is to let the service start a JTA transaction. This will automatically be available to all code running in the same thread. Then use a managed connection pool, so connections automatically join the running JTA transaction.
You can do your usual JDBC work on the connections, just don't call commit() on them, but do call close() on them.
If your goal is only to understand persistence and you don't necessarily want to think about transactions at this moment, you can use stateless session beans (EJB beans). These will automatically start and commit the transaction for you. The Service can be the EJB that your clients will call and this one will automatically start the transaction. Your DAOs can be stateless session beans as well and can be injected with a data source.
e.g.
#Stateless
public class MyService {
#EJB
private UserDAO userDAO;
#EJB
private GroupDAO groupDAO;
#EJB
private RoleDAO roleDAO;
public void myMethod() {
User user = userDAO.getById(...);
Group group = groupDAO.getByUserId(...);
// more DAO stuff
}
}
#Stateless
public class UserDAO {
#Resource(mappedName="java:/myDS")
private DataSource dataSource;
public void getById(Long id) {
connection = dataSource.getConnection();
// do JDBC work on connection
}
}
It's easy to let DAOs share a connection, because it should be created by a service layer, passed into each DAO object, and closed. Just pass the connection around.
Another alternative would be to not share the connection between three DAOs. Rather than mke three queries, you could make one JOIN query, bring all the data back at once, and map it into objects. The penalty is more bytes on the wire, because you bring the parent data back for every child object.
Use a thread local to hold a reference to your connection. The DAO will use connection from the service layer when a connection is passed. When the caller (service layer) does not pass a connection , DAO will use the connection from thread-local. Thus all DAO can share same connection in a given thread.
TX management is as follows.
All transactions are initiated and ended in service layer. The DAO will not have any commit/rollback logic. Since connection is shared across all the DAO, the caller (service layer) is in full control of the transaction.
I have a Transaction problem on Spring 3.0.5. In my case I get the so-called exception "No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here"... I have tried everything so far. I can see in my log that the transactional services are detected and registered as Spring beans and I can also see in the logs that they are proxied and associated with underlying Spring transaction interceptors. (Advise) When I run my Spring MVC app my controller will call the service...... :-( but the transaction interceptors are not triggered. (??) I expect my Spring service proxy to open a session and start a transaction before calling my target service method. Since this does not happen, I get the above exception. I have been almost two days on this problem. Tried everything which I found on the internet...but in vain.
I have layered architecture: presentation (springmvc), service (transaction annotated), dataacess (Spring/Hibernate classic sessionfactory). My model objects are annotated with jpa (javax.persistence.*). My spring context config files are separated in appContext.xml, appContext-service.xml and appContext-dao.xml. I have defined my Spring LocalSessionFactoryBean, Datasource and TransactionManager (HibernateTransactionManager) in appContext-dao.xml. I have put in appContext-service.xml where my service implementations resides. In all of my config files I have included and to detect my beans through Controller, Service and Repository annotations.
I appreciate any kind of help.
It sounds like you are doing everything correctly and you know what you are doing. There's not much we can do here unless you show some configuration.
What I'd suggest is some debugging.
First: do you have Unit tests in the service layer that test the queries you are using? Perhaps you can find the error in the service layer.
Then: debug the MVC app, check the types of the injected services. Verify that they are proxies, not the original types.
If they are the original types, you
have an error in your transaction
configuration .
If they are proxies, step through the
query methods and verify that the
transaction logic is applied.
This sounds like accessing a lazily-loaded list or set of you dao after the closing of the transaction. This typically happens if you access that list in the view in stead of the controller, as your controller probably calls methods in transaction scope, and then leaves the transaction and forwards the loaded bean to the view.
Simple solutions:
Configure your data bean to eagerly load
Force loading of the dependencies in the controller (just loop through them)
have a look at this article ans possibly also quite a few right here on SO on lazy loading / lazy fetching of one-to-many associations and the like
Imagine:
// your dao
public class Foo {
// lots of other properties
List<Something> stuff;
// getters and setter for stuff
}
// repository
#Transactional
public class FooRepo {
public Foo loadFoo(int id) {
...
}
}
// Controller
public class Controller {
public void Control() {
sessionContext.set("foo", repo.loadFoo(1)); // transaction managed by spring
}
public void setFooRepo(FooRepo repo) {this.repo = repo};
}
// View
for (Something something : foo.getStuff()) {
// Ooops transaction is closed! stuff is lazily loaded so NOT AVAILABLE
// practical solutions:
// - do not load lazily (Foo.hbm.xml)
// - or force loading by getting all Somethings in the controller
}