I have written 2 WebSocket ServerEndpoints that inject Services that themselves interact with the Database using injected instances of the JPA EntityManager.
The application is a web application deployed on a Tomcat Server, using Jersey as JAX-RS implementation and Hibernate as JPA Provider.
Sometimes it happens that the EntityManager is closed when trying to the DB inside the Endpoints. Also I fear I might have produced code that triggers a Memory Leak.
This is the custom ServerEndpoint.Configurator that I am using (based on https://gist.github.com/facundofarias/7102f5120944c462a5f77a17f295c4d0):
public class Hk2Configurator extends ServerEndpointConfig.Configurator {
private static ServiceLocator serviceLocator;
public Hk2Configurator() {
if (serviceLocator == null) {
serviceLocator = ServiceLocatorUtilities.createAndPopulateServiceLocator();
ServiceLocatorUtilities.bind(serviceLocator, new ServicesBinder()); // binds the "normal" Services that interact with the DB
ServiceLocatorUtilities.bind(serviceLocator, new AbstractBinder() {
#Override
protected void configure() {
bindFactory(EntityManagerFactory.class).to(EntityManager.class);
}
});
}
}
#Override
public <T> T getEndpointInstance(final Class<T> endpointClass) throws InstantiationException {
T endpointInstance = super.getEndpointInstance(endpointClass);
serviceLocator.inject(endpointInstance);
return endpointInstance;
}
}
In the rest of the application I am using the same ServicesBinder, but a different Binder for the EntityManager.
The EntityManagerFactory looks like this:
public class EntityManagerFactory implements Factory<EntityManager> {
private static final javax.persistence.EntityManagerFactory FACTORY = Persistence.createEntityManagerFactory("PersistenceUnit");
#Override
public final EntityManager provide() {
return FACTORY.createEntityManager();
}
#Override
public final void dispose(final EntityManager instance) {
instance.close();
}
}
It is loaded with the Scope RequestScoped (only there, not in the WebSocket Endpoints).
I tried creating an EntityManager instance for every access in my DAOs, but then I would run into org.hibernate.LazyInitializationExceptions eventually since my DTOs need an open EntityManager (implicitly).
Any suggestions on how to circumvent the issues I'm having?
Okay, I managed to fix my issue by simply rewriting the EntityManager handling to create and close an EntityManager every time I interact with the database.
Related
I have a service with a persistence setup using JPA, Hibernate and Guice (if it's useful, I'm not using Spring). This is the first, working version of my code:
public class BookDao {
#Inject
protected Provider<EntityManager> entityManagerProvider;
protected EntityManager getEntityManager() {
return entityManagerProvider.get();
}
#Transactional
public void persist(Book book) {
getEntityManager().persist(book);
}
}
public class MyAppModule extends AbstractModule {
#Override
protected void configure() {
initializePersistence();
}
private void initializePersistence() {
final JpaPersistModule jpaPersistModule = new JpaPersistModule("prod");
jpaPersistModule.properties(new Properties());
install(jpaPersistModule);
}
}
But now I need to configure multiple persistence units. I'm following the advice in this mailing list, and according to them, I should move my module logic to a private module. I did as suggested and created a second version of the same code, the changes are commented below:
#BindingAnnotation
#Retention(RetentionPolicy.RUNTIME)
#Target({ FIELD, PARAMETER, METHOD })
public #interface ProductionDataSource {} // defined this new annotation
public class BookDao {
#Inject
#ProductionDataSource // added the annotation here
protected Provider<EntityManager> entityManagerProvider;
protected EntityManager getEntityManager() {
return entityManagerProvider.get();
}
#Transactional
public void persist(Book book) throws Exception {
getEntityManager().persist(book);
}
}
public class MyAppModule extends PrivateModule { // module is now private
#Override
protected void configure() {
initializePersistence();
// expose the annotated entity manager
Provider<EntityManager> entityManagerProvider = binder().getProvider(EntityManager.class);
bind(EntityManager.class).annotatedWith(ProductionDataSource.class).toProvider(entityManagerProvider);
expose(EntityManager.class).annotatedWith(ProductionDataSource.class);
}
private void initializePersistence() {
JpaPersistModule jpaPersistModule = new JpaPersistModule("prod");
jpaPersistModule.properties(new Properties());
install(jpaPersistModule);
}
}
The newly annotated EntityManager is being correctly injected by Guice and is non-null, but here's the fun part: some of my unit tests started failing, for example:
class BookDaoTest {
private Injector injector;
private BookDao testee;
#BeforeEach
public void setup() {
injector = Guice.createInjector(new MyAppModule());
injector.injectMembers(this);
testee = injector.getInstance(BookDao.class);
}
#Test
public void testPersistBook() throws Exception {
// given
Book newBook = new Book();
assertNull(newBook.getId());
// when
newBook = testee.persist(newBook);
// then
assertNotNull(newBook.getId()); // works in the first version, fails in the second
}
}
In the first version of my code the last line above just works: the entity is persisted and has a new id. However, in the second version of my code (using a PrivateModule and exposing an annotated EntityManager from it) the persist() operation doesn't work anymore, the entity is without an id. What could be the problem? I didn't do any other configuration changes in my environment, and I don't see error messages in the logs. Let me know if you need more details.
It turns out that the problem was the #Transactional annotation. In the first version of my code, Guice automatically adds interceptors for managing the transaction. By doing a debug, I found out that before executing my persist(Book book) method, Guice calls the following method from the com.google.inject.internal.InterceptorStackCallback package:
public Object intercept(Object proxy, Method method, Object[] arguments, MethodProxy methodProxy)
In the second version of my code, when I exposed the persistence unit from a private module the above interceptor was no longer called, leaving my persist operation without transaction handling. This is a known issue and is by design.
As a workaround I had to implement transactions by hand, making my code more verbose. I also had to change the way the entity manager is injected. This solution worked for me:
public class BookDao {
#Inject
#Named(PROD_PERSISTENCE_UNIT_NAME)
private EntityManagerFactory entityManagerFactory;
private EntityManager getEntityManager() {
return entityManagerFactory.createEntityManager();
}
public void persist(Book book) throws Exception {
EntityManager em = getEntityManager();
try {
em.getTransaction().begin();
em.persist(book);
em.getTransaction().commit();
} catch (Exception e) {
em.getTransaction().rollback();
throw e;
} finally {
em.close();
}
}
}
public class MyAppModule extends PrivateModule {
public static final String PROD_PERSISTENCE_UNIT_NAME = "prod";
#Override
protected void configure() {
initializePersistence();
}
private void initializePersistence() {
// persistence unit set to prod DB
final JpaPersistModule jpaPersistModule = new JpaPersistModule(PROD_PERSISTENCE_UNIT_NAME);
// connection properties set to suitable prod values
jpaPersistModule.properties(new Properties());
install(jpaPersistModule);
// expose bindings to entity manager annotated as "prod"
bind(JPAInitializer.class).asEagerSingleton();
bind(PersistService.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME)).to(PersistService.class).asEagerSingleton();
expose(PersistService.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME));
bind(EntityManagerFactory.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME)).toProvider(binder().getProvider(EntityManagerFactory.class));
expose(EntityManagerFactory.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME));
bind(EntityManager.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME)).toProvider(binder().getProvider(EntityManager.class));
expose(EntityManager.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME));
bind(UnitOfWork.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME)).toProvider(binder().getProvider(UnitOfWork.class));
expose(UnitOfWork.class).annotatedWith(named(PROD_PERSISTENCE_UNIT_NAME));
}
}
As a lesson, be very watchful around annotations and other such "magic" that modifies your code under the hood, finding bugs becomes quite difficult.
I've a Spring Boot application with the Spring Data JPA and two entities mapped to a database. My application is set to recreate the database on every startup. Now i want to create some instances of these POJOs, persist them in the database so I've some data to test my application with in the ongoing development process.
What is the best way to do that?
What i tried so far:
My class to add the sample data
public class DBSampleAdditor {
#PersistenceContext
private EntityManager em;
#Transactional
public void addSampleData() {
// creating POJO instances and persist them with em.perists()
}
}
I tried to call these functions in the main of my ApplicationClass
#SpringBootApplication()
public class ApiApplication {
public static void main(String[] args) {
SpringApplication.run(ApiApplication.class, args);
DBSampleAdditor dbsa = new DBSampleAdditor();
dbsa.addSampleData();
}
}
That didnt work at all, as the EntityManager never got an Instance from the persistence unit.
I also tried to create a CommandLineRunner:
#Component
public class PTCommandLineRunner implements CommandLineRunner {
#Override
public void run(String... args) throws Exception {
System.out.println("Adding sample data...");
DBSampleAdditor dbsa = new DBSampleAdditor();
dbsa.addSampleData();
}
}
But that one seemed to never been called in the startup process.
You can use method with #PostConstruct with #Component to insert data at startup.
#PostConstruct will be invoked after the bean has been created, dependencies have been injected, all managed properties are set, and before the bean is actually set into scope.
Just inject your database repository in the class marked with #Component and call your injected database repository inside the method marked with #PostConstruct
Example:
#Component
public class DbInit {
#Autowired
private UserRepository userRepository;
#PostConstruct
private void postConstruct() {
User admin = new User("admin", "admin password");
User normalUser = new User("user", "user password");
userRepository.save(admin, normalUser);
}
}
In order to use spring managed components like the EntityManager the objects need to spring Beans, which basically means they are created inside the spring context and can utilise the IoC container.
In the example above, running the DbSampleAdditor class from the main method is outside the spring context, so the beans like PersistenceContext wont get injected.
One fairly simple way to do this inside the spring context is to add an ApplicationListener for the ApplicationReadyEvent
#Component
class Initialise implements ApplicationListener<ApplicationReadyEvent> {
#Autowired
private final DbSampleAdditor db;
#Override
public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
db.addSampleData();
}
}
When the application ready event fires all the necessary spring bean wiring is set up so when it runs the addSampleData method, things like the EM are good to go.
Other approaches for setting up a DB for spring boot are documented here
Instead of creating an object DBSampleAdditor, try to autowire it so that it is available on application startup. #Autowired DBSampleAdditor DBSampleAdditor Then you try to add sample data within the run method
I am trying to Inject a class into my entity listener created by my jpa provider. The following code throws a PersistenceException - unable to build hibernate SessionFactory (which I expected). From my research it seems like jpa 2.1 supports dependency injection. I am missing some type of binding/provides for guice to know about this class. My guess is it has to be some type of run time binding.
public class EntityListener {
private SomeService service;
#Inject
public EntityListener(SomeService service) {
this.service = service;
}
#PostLoad
public void load(Object o){
//use service here
}
}
Is there any analog of OpenEntityManagerInViewInterceptor for CLI application?
I am trying to use Spring's CrudRepository with JPA data source backed by Hibernate 4 in CLI application.
My main method creates an instance of the class containing this method and injects services using context.getBeanFactory().autowireBean(object);.
Services for data fetching have methods annotated with #Transactional. These methods invoke CrudRepository's methods.
But I receive org.hibernate.LazyInitializationException when I try to manage related entities in the CLI application.
Is there any workaround to have lazy loading working in the CLI application outside the #Transactional methods like OpenEntityManagerInViewInterceptor for web applications?
Look at the following snippet:
public class test {
#Autowired
public UserService userService;
public static void main(String[] args) {
test test = new test();
//injecting dependencies into test
test.run();
}
private void run() {
User user = userService.findById(42);
System.out.println(user.getLogin()); //User was fetched successfully
Address address = new Address("London");
user.addAddress(address);//Exception in thread "main" java.lang.RuntimeException: org.hibernate.LazyInitializationException: failed to lazily initialize a collection
}
}
I would say there isn't one for CLI because there's no notion of View as in MVC pattern. However I suspect your problem is due to ineffective declarative transaction management.
For a CLI application, ensure you have properly setup datasource, EntityManagerFactory and declarative transaction managemeent.
A good practice is to enclose your business / DAO code in service / repository classes annotated with #Service, #Repository, #Component or other appropriate spring annotation, create your own ApplicationContext and obtain reference to your services:
// UserDAO.java ----------------------------------------
#Repository
public class UserDAO {
#PersistenceContext private EntityManager em;
#Transactional
public User findById(long id) {
// ...
}
}
// UserService.java ----------------------------------------
#Service
public class UserService {
#Autowired private UserDAO userDAO;
// ...
}
// MainClass.java ----------------------------------------
public class MainClass {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("META-INF/root-context.xml");
UserService userService = context.getBean(UserService.class);
// more code here..
context.close();
}
}
Autowiring and declarative transaction will still work as per normal as long as the bean is created by Spring container.
I am learning Java EE and JSP. I have created an Enterprise Application project in NetBeans.
I have the EJB project where all beans are and a WAR project where all web/client stuff is.
My problem is that the annotation #EJB does not instantiate my Bean in the WAR application. Can I use #EJB outside the EJB application?
In the EJB project, I have these files:
CustomerRemote.java
#Remote
public interface CustomerRemote {
public Customer createCustomer();
public Customer getCustomer(int customerId);
public void removeCustomer();
}
CustomerBean.java
#Stateless
public class CustomerBean implements CustomerRemote {
#PersistenceContext(unitName="testPU")
private EntityManager entityManager;
#Override
public Customer createCustomer() {
throw new UnsupportedOperationException("Not supported yet.");
}
#Override
public void removeCustomer() {
}
#Override
public Customer getCustomer(int customerId) {
throw new UnsupportedOperationException("Not supported yet.");
}
}
In the WAR project, I have a file that my JSP page uses to communicate with the EJB stuff. The problem is that the CustomerRemote object is never instantiated. #EJB annotation does not seem to work because customerRemote always is null. But when instantiating it with the lookup method, it works! So why does not #EJB work?
public class CustomerProxyBean {
#EJB
private CustomerRemote customerRemote;
public CustomerProxyBean() {
// try {
// InitialContext context = new InitialContext();
// customerRemote = (CustomerRemote)context.lookup("java:app/SimpleShop-ejb/CustomerBean");
//
// } catch (NamingException ex) {
// Logger.getLogger(CustomerProxyBean.class.getName()).log(Level.SEVERE, null, ex);
// }
}
#EJB annotation will work only in cases where your class is container-managed, that is EJB, servlet, JSP... In your case you put it into plain old Java object (POJO) so injection will not work, as you have experienced. Write your CustomerProxyBean as a stateless session bean, and you'll see the change.
Alternatively, if you want to avoid JNDI for some reason, you can use CDI and #Inject annotation to inject EJB and achieve wished behaviour, even in POJO:
#Inject
private CustomerRemote customerRemote;