I'm trying to get a better handle on the use of Spring's #Transactional attribute. I understand that it basically wraps the contents of the method marked as #Transactional in a transaction. Would it be appropriate to mark a service/business layer method as transactional, as opposed to the actual DAO method, as I have done here?
Service Implementation
public class UserServiceImpl implements UserServiceInt{
#Autowired
private UserServiceDAO serviceDAO;
#Override
public User getUser(int id){
return serviceDAO.getUser(id);
}
#Override
#Transactional
public void updateUserFirstName(int id, String firstName) throws SomeException{
User userToUpdate = getUser(id);
if(userToUpdate == null){
throw new SomeException("User does not exist");
}
userToUpdate.setFirstName(firstName);
serviceDAO.updateUser(userToUpdate);
}
}
DAO Implementation
public class UserServiceDAOImpl implements UserServiceDAOInt{
#PersistenceContext(unitName="myUnit")
private EntityManager entityManager;
#Override
public void updateUser(User user){
entityManager.merge(user);
}
}
I'm not even sure if the call to merge is even necessary. How does Spring know which EntityManager to use since there isn't an EntityManager declare in the UserServiceImpl class?
We mark a Service layer with #Transactional when a method in a Service class is having multiple database calls and we want either all calls should happen or no one should happen or we can say if any call fail then whole transaction should rollback. If we are not falling under this criteria then we can opt for #Transactional on DAO layer also.
How does Spring know which EntityManager to use since there isn't an EntityManager declare in the UserServiceImpl class?
Spring is referring the EntityManager from persistence.xml(from classpath), whose structure is similar to below:
<?xml version="1.0" encoding="UTF-8"?>
<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_1_0.xsd" version="1.0">
<persistence-unit name="myUnit">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:/YourDatasource</jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.show_sql" value="true" />
</properties>
</persistence-unit>
</persistence>
Related
I have User and Post entities with a unidirectional relationship. I am getting org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: exception when I try to get all posts from a specific user.
According to these SO answers, the optimal way to handle this is to use #Transactional annotation for the service method/class. Placing annotations does not work for me. I am using Wildfly server, Hibernate, MySQL, and Java EE MVC web framework.
How do I make it work, i.e. get the posts from a user? I managed to do it via eager loading, but this is not recommended for performance reasons.
#Transactional
public class UserService {
private List<User> users;
private Set<Post> posts;
#PersistenceContext(unitName = "my-pu")
private EntityManager em;
public List<User> getUsers() {
String qlQuery = "SELECT u FROM User u";
Query query = em.createQuery(qlQuery);
users = query.getResultList();
return users;
}
#Transactional
public Set<Post> getUserPosts(Long userId) {
String qlQuery = "SELECT u FROM User u WHERE u.id = :userId";
Query query = em.createQuery(qlQuery);
query.setParameter("userId", userId);
User user = (User) query.getSingleResult();
posts = user.getPosts();
return posts;
}
}
This is my Service method.
#Path("users")
#Controller
public class UserController {
#Inject
private Models models;
#Inject
private UserService service;
#GET
#Produces("text/html")
#View("showUsers.ftl")
public void users() {
List<User> users = service.getUsers();
models.put("users", users);
}
#GET
#Path("{id}")
#Produces("text/html")
#View("showUserPosts.ftl")
public void getPosts(#PathParam("id") Long userId) {
System.out.println("here am i");
Set<Post> posts = service.getUserPosts(userId);
models.put("posts", posts);
}
}
This is my controller.
<?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="my-pu" transaction-type="JPA">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<properties>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/testdb?useSSL=false"/>
<property name="javax.persistence.jdbc.user" value="testuser"/>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.password" value="test623"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/>
<property name="hibernate.dialect.storage_engine" value="innodb"/>
<property name="javax.persistence.schema-generation.database.action"
value="drop-and-create"/>
<property name="javax.persistence.sql-load-script-source"
value="META-INF/sql/data.sql" />
</properties>
</persistence-unit>
</persistence>
An this is my persistence unit.
Error message:
org.jboss.resteasy.spi.UnhandledException: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.zetcode.model.User.posts, could not initialize proxy - no Session
at org.jboss.resteasy.core.ExceptionHandler.handleException(ExceptionHandler.java:257)
at org.jboss.resteasy.core.SynchronousDispatcher.writeException(SynchronousDispatcher.java:195)
at org.jboss.resteasy.core.SynchronousDispatcher.writeResponse(SynchronousDispatcher.java:539)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:461)
at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:231)
at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:137)
at org.jboss.resteasy.core.interception.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:361)
at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:140)
at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:217)
at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:227)
at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:56)
at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:51)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74)
at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
at io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:67)
at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
You are returning an uninitialized collection proxy out of session boundaries (demarcated by the transaction in this case).
You can initialize the proxy by calling Hibernate.initialize(posts) or by just calling posts.size() before returning it (the first approach more clearly describes the intent imo).
An alternative that I use most often is to utilize DTOs, so that I don't have to deal with detached objects, but also because of the ability to tailor presentation objects (and web service responses in general) to the exact needs of clients using them. Also that way the domain model (Hibernate entities) is decoupled from the presentation logic, allowing the two to evolve independently.
Other alternatives include Open Session in View (anti-?) pattern and hibernate.enable_lazy_load_no_trans property, but they are less flexible and have their own pros and cons.
My application is running perfectly fine in JBoss AS 5.1.0. My goal is to migrate it to Wildfly 10.
Below is my persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<persistence-unit name="ApplicationPersistenceUnit">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:/MySQL_DS</jta-data-source>
<jar-file>application_db_interface.jar</jar-file>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
<property name="jboss.entity.manager.jndi.name" value="java:/ApplicationPersistenceUnit"/>
<property name="jboss.entity.manager.factory.jndi.name" value="java:/ApplicationPersistenceUnitFactory"/>
</properties>
</persistence-unit>
</persistence>
Below is my transaction manager class;
public class TransactionManager implements TransactionManagerI {
#PersistenceContext(unitName="ApplicationPersistenceUnit")
private EntityManager em;
...
}
I also try to use lookup but it still returns null;
public Session getSession() throws DAOException {
..
em = (EntityManager) new InitialContext().lookup("java:/ApplicationPersistenceUnit");
..
}
Below is the project schema;
->application.ear
->application.ear/application.war
->application.ear/db.jar
->application.ear/db.jar/META-INF/persistence.xml
I looked into other solutions and try one by one but couldn't figure out mine yet. I can provide more info if you need.
UPDATE: I check JNDI view from wildfly admin console. I don't see ApplicationPersistenceUnit there. It looks like server doesn't bind my persistence.xml file. I am now trying to figure out this problem.
I'm new in JavaEE. I'm trying to make webapp using JPA with DAO and Service layer. Part of the task is not to use Spring and Hibernate. So i can use only JPA.
As i understand, Service and Dao should be a Singletones. I should get EntityManager on each database operation, and close it after. I'm using GenericDao for some operation, and EntityManager is needed in Dao, as a field.
How to use it? How can i get entityManager in Service, start transaction in service, and pass it inside the DAO implementation?
Here's example of my DAO interface.
public interface GenericDAO<T> {
void save(T entity);
void merge(T entity);
void delete(T entity);
...
}
I can get EntityManager in Service. In implementation all these method's will be using EntityManager. So i need it there as a field.
So how can i pass EntityManager in my DAO implementation and still make all this construction thread safe?
I don't think make inerface method like this is a good idea:
void save(T entity, EntityManager entityManager);
void merge(T entity, EntityManager entityManager);
EntityManager without Hibernate? What will make the ORM to it? Maybee you should use JDBC in the DAL instead of the EntityManager without Hibernate.
Using only JPA is a good goal. Spring has been around a long time and has a very vendor specific implementation of data repositories. Hibernate is much closer to JPA (in my opinion), but is also quite specific. The EntityManager is effectively a generic DAO as you describe it. It has persist, merge, find, and delete methods, as well as many others.
In order to learn JPA a bit you can create a simple project with a simple entity and go from there. I have many such "playground" projects, but my simplest one is courtesy of Geoffroy Warin
A main App class:
public class App {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("persistence");
EntityManager em = emf.createEntityManager();
try {
EntityTransaction tx = em.getTransaction();
tx.begin();
User user = new User();
em.persist(user);
tx.commit();
System.out.println("user=" + user);
User found = em.find(User.class, 1L);
System.out.println("found=" + found);
} finally {
emf.close();
}
}
}
A simple User entity:
#Entity
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id #GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
public Long getId() { return id; }
#Override
public String toString() {
return "User:"+id;
}
}
and a persistence.xml (goes in resources/META-INF directory):
<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="persistence" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<class>model.User</class>
<properties>
<property name="javax.persistence.jdbc.driver" value="org.hsqldb.jdbcDriver" />
<property name="javax.persistence.jdbc.url" value="jdbc:hsqldb:mem:standalone" />
<property name="javax.persistence.jdbc.user" value="sa" />
<property name="javax.persistence.jdbc.password" value="" />
<property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect" />
<property name="hibernate.hbm2ddl.auto" value="create" />
<property name="hibernate.show_sql" value="true" />
</properties>
</persistence-unit>
</persistence>
And, of course, a POM file to bring in all the dependencies.
<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>jpa-playground</groupId>
<artifactId>jpa-playground</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>4.3.11.Final</version>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.2.9</version>
</dependency>
</dependencies>
</project>
Figure out how to put all this together and you can play with JPA to your hearts content. Note that is uses Hibernate as the JPA provider, but it does not use any hibernate specific constructs.
Creating a service layer is usually done with EJB's, e.g., stateless and statefull beans, and for that you need a container like Wildfly or Tomcat EE, among others. However, you can simulate them by creating a class called ServiceLayer and putting methods like createUser, changeUserName, removeUser and things like that in it. It would use the EntityManager on behalf of the application.
I am using JPA and java as technology and writing very simple web application.
There is one class called Employee, look like below code
#Entity
#Table(name="Employee")
public class Employee{
#Id
private int id;
#Column(name="name")
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
one table called Employee is exist into database.
Its persistence.xml file looks like below code.
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
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">
<persistence-unit name="JPAService" transaction-type="RESOURCE_LOCAL">
<!-- Persistence provider -->
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<!-- Entity classes -->
<class>com.solution.domain.Employee</class>
<properties>
<!-- The JDBC driver of your database -->
<property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver" />
<!-- The JDBC URL to the database instance -->
<property name="javax.persistence.jdbc.url" value="jdbc:postgresql://localhost:5432/test" />
<!-- The database username -->
<property name="javax.persistence.jdbc.user" value="postgres" />
<!-- The database password -->
<property name="javax.persistence.jdbc.password" value="postgres"/>
<property name="javax.persistence.jdbc.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />
</properties>
</persistence-unit>
</persistence>
Employee class entry exist in this xml file.
There are not issue while startup of application. Every thing working fine.
But when query is executed then it throw and exception.
"Employee is not mapped [select e from Employee e]"
EntityManager em = getEntityManager();
em.getTransaction().begin();
Query query = em.createQuery("select e from Employee e");
List<Employee> employees = query.getResultList();
if(employees != null){
return employees.get(0);
}
em.getTransaction().commit();
I am unable to understand where mapping is missing. As i already mentioned its a web project. So for more clarification i am attaching one screen shot of my project too.
Thanks in advance.
I got the problem. It was an silly mistake. I was using JPA but in Employee class I used the Entity annotation from hibernate implementation instead of JPA package.
Side note: It's a bad idea to model the database with JPA, e.g., by implementing the integer surrogate key in the data model. JPA is supposed to help persist an object model, which would use the natural "key" to distinguish objects. A JPA entity should only expose attributes of the object model. This has the benefit of greatly simplifying the expression of relationships because you relate objects, not ID fields. Don't use JPA for database translation, use it for object persistence.
I assume there's a way to use an embedded Java DB instance within a standalone Felix 4.0.2 OSGi box. However I can't seem to find any clues as to how I should setup the whole thing: What implementation bundles should I use? How should I set it up?
I tried quite a few things, including getting the dedicated bundles from a glassfich 3.1.2 install, but whatever I do no persistence manager is registered by the framework to provide my app with.
My code otherwise follows all academical precepts, with a a ServiceTracker being registered to find out about EntityManagerFactory registrations, but none is found...
Here's my persistence.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0"
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_1_0.xsd">
<persistence-unit name="com.julianware.toolbox.logbacktracker.pu"
transaction-type="RESOURCE_LOCAL">
<!-- mapping-file>META-INF/maintenance_orm.xml</mapping-file>
<mapping-file>META-INF/vehicle_orm.xml</mapping-file -->
<class>com.julianware.toolbox.logbacktracker.pu.LoggerEvent</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="javax.persistence.jdbc.driver" value="org.apache.derby.jdbc.EmbeddedDriver" />
<property name="javax.persistence.jdbc.url" value="jdbc:derby:database;create=true" />
<property name="javax.persistence.jdbc.user" value="felix" />
<property name="javax.persistence.jdbc.password" value="felix" />
</properties>
</persistence-unit>
And my Activator's start() method:
public void start(final BundleContext context) throws Exception {
Filter filter = context.createFilter(String.format("(&(%s=%s)(%s=%s))",
OBJECTCLASS, EntityManagerFactory.class.getName(),
EntityManagerFactoryBuilder.JPA_UNIT_NAME, "com.julianware.toolbox.logbacktracker.pu")
);
entityManagerFactoryTracker = new ServiceTracker(context, filter, new ServiceTrackerCustomizer() {
#Override
public void removedService(ServiceReference reference, Object service) {}
#Override
public void modifiedService(ServiceReference reference, Object service) {}
#Override
public Object addingService(ServiceReference reference) {
logger.debug("Found Entity Manager Service reference.");
entityManagerFactory = (EntityManagerFactory) context.getService(reference);
return entityManagerFactory;
}
});
entityManagerFactoryTracker.open();
}
Last, I'm on JDK 7. Java DB implementation is that of the jdk install.
Does anyone know anything?
Mighty thanks.
Julian
I'm not sure, but you could try Derby from Maven Central. It should contain the correct OSGi meta data.