Persisting JPA entity using plain EntityManager in spring boot in #PostConstruct - java

I have a minimal spring boot application, consisting of 3 classes: an Entity, a component that tries to populate db in #PostConstruct and an application class. Nothing else.
#SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
#Component
#Transactional
public class Initializer {
#Autowired
EntityManager em;
#PostConstruct
public void populate() {
em.persist(new MyEntity());
}
}
#Entity
public class MyEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
int id;
}
When I run the application I get an javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread - cannot reliably process 'persist' call
I'm not the only one ever getting that error and I read a lot of the posts, but did not find a magic solution.
If I autowire an EntityMananagerFactory and instead do:
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
em.persist(new MyEntity());
em.getTransaction().commit();
em.close();
It works. Question is: Is there a simpler way (place the right annotation at the right spot) to get an EntityManager that can persist an entity? I have good reasons for not creating a repository (I tried doing that and that works).
Best regards Jens

So after trying a lot of different stuff, I think that I found a working solution, where the initalization is done in an ApplicationReadyEvent handler instead of in a #PostConstruct method:
#Component
public class Initializer {
#PersistenceContext
EntityManager em;
#EventListener(ApplicationReadyEvent.class)
#Transactional
public void init() {
em.persist(new MyEntity());
}
}
Working example: https://github.com/djarnis73/spring-boot-db-init-with-jpa-entity-manager

As per my understanding, #PostConstruct gets called on app startup when we want to initialize the beans and the configs. I think #PostConstruct is not the right place to do so.
However you can use #PersistenceContext on your entityManger instead of autowiring it.

Related

How to add sample data to database with springboot?

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

Proliferation of EntityManager in EJB 3

Consider this scenario:
#Stateless
public class A{
#PersistenceContext
private EntityManager em;
#EJB
B b;
#EJB
C c;
public void doSomeWork(){
b.doSomeWork();
c.doSomeWork();
em.persist(someStuff);
}
}
#Stateless
public class B{
#PersistenceContext
private EntityManager em;
public void doSomeWork(){
//Do stuff
em.persist(stuff)
}
}
#Stateless
public class C{
#PersistenceContext
private EntityManager em;
public void doSomeWork(){
//Do stuff
em.persist(stuff)
}
}
In this scenario I'm using three different EntityManagers for the same transaction (started in method doSomeWork() from class A
Now, the same transaction but with only one EntityManager:
#Stateless
public class A{
#PersistenceContext
private EntityManager em;
#EJB
B b;
#EJB
C c;
public void doSomeWork(){
b.setTheEntityManager(em);
b.doSomeWork();
c.setTheEntityManager(em);
c.doSomeWork();
em.persist(someStuff);
}
}
#Stateless
public class B{
private EntityManager em;
public void setTheEntityManager(EntityManager em){
this.em = em;
}
public void doSomeWork(){
//Do stuff
em.persist(stuff)
}
}
#Stateless
public class C{
private EntityManager em;
public void setTheEntityManager(EntityManager em){
this.em = em;
}
public void doSomeWork(){
//Do stuff
em.persist(stuff)
}
}
I've been reading about the EntityManager lifecycle but I just can't get if there's any advantage (in terms of performance, database connections,..) in the second scenario. My first answer would be yes because there's only one EntityManager but on the other hand I don't see any code examples using this approach (passing the EntityManager from EJB to EJB). Also, the Oracle Docs say "By automatically propagating the persistence context, application components don’t need to pass references to EntityManager instances to each other in order to make changes within a single transaction. The Java EE container manages the lifecycle of container-managed entity managers."
Anything you #Inject or #Resource from the container should never ever be passed somewhere else. That's a surefire way to create a memory leak and/or undefined behavior. There also is no advantage to doing the second, as the container automatically injects the correct EntityManager for you every time :)
So in short, never ever do the second scenario, always do the first scenario. The container is actually inject a Proxy that routes you to the correct (the same in your case since you haven't exited your transaction boundary) entity manager.
What's happening is the call into a.doSomeWork() has an implied TransactionAttribute.Required. If a transaction has not already been started, the container starts one for you. Subsequent calls to B and C now participate in that transaction. When you exit a.doSomeWork(), assuming that's where the transaction started, that's where the transaction commits. Anything done by A, B, and C all occur in the same transaction.

JPA EntityManager blocks after #Transactional method

I have a Spring-JUnit test with a setup method that uses JPA Entities to setup test data. Within the test itself, legacy code uses JDBC-Templates to query the inserted test data. When the legacy code issues a query, the call to jdbcTemplate.query(...) hangs.
My Unit Test looks like this:
#TransactionConfiguration(defaultRollback = false)
#ContextConfiguration(locations = { "/testContext.xml" })
#Transactional
public class MyTest {
#PersistenceContext(unitName = "someUnit")
private EntityManager entityManager;
#Test
public void test() {
// here some legacy Code is called that uses JDBC Templates to query
// the inserted test data. The legacy code hangs upon jdbcTemplate.query(...)
}
#Before
public void before() {
this.entityManager.persist(new Entity1(...));
this.entityManager.persist(new Entity2(...));
}
}
My question is: Why doesn't the enitymanager commit upon exiting the before() method? Or does it commit and immediately start a new transaction, that still references the stored entity? I also tried to do without the junit annotation #Before annotation and manually call the before() method. But this gives the same result.
Any advice is highly appreciated.
Test class
#ContextConfiguration(locations = { "/testContext.xml" })
#TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = false)
#RunWith(SpringJUnit4ClassRunner.class)
#Transactional
public class MyTest extends AbstractTransactionalJUnit4SpringContextTests{
#PersistenceContext(unitName = "someUnit")
private EntityManager entityManager;
#Autowired
private BeforeService beforeService;
#Test
public void test() {
beforeService.before();
// here some legacy Code is called that uses JDBC Templates to query
// the inserted test data. The legacy code hangs upon jdbcTemplate.query(...)
}
}
Before Service
public class BeforeService {
private EntityManager entityManager;
#PersistenceContext
public void setEntityManager(final EntityManager entityManager) {
this.entityManager = entityManager;
}
#Transactional(propagation=Propagation.REQUIRES_NEW)
public void before() {
entityManager.persist(new Entity1(...));
entityManager.persist(new Entity2(...));
}
}
I hope I've given you all the answers about your question.

Can I get something like an entity bean in EJB 3.2?

I was reading a Java EE book recently, and apparently entity beans were recently removed from the EJB specification. You are supposed to use JPA instead. But I want entity beans!! What I am really looking for is a JPA persistent entity that is remotely accessible, like an EJB. Something like this:
#Entity
#Remote(MyEntityRemote.class)
#LocalBean
public class MyEntityEJB implements MyEntityRemote {
public void doSomething() {
// actually do something
}
}
Is this at all possible without removing the bean annotations and writing a session bean like this:
#Stateless
#Remote(StatelessInterfaceToMyEntityRemote.class)
#LocalBean
public class StatelessInterfaceToMyEntity implements StatelessInterfaceToMyEntityRemote {
public void doSomething(MyEntity entity) {
entity.doSomething();
}
}
If I understand you correctly it is possible
first you create an Entity:
#Entity
#Table('MyEntityTable')
public class MyEntity {...}
Then you create a session bean facade for the entity exposing through it any interfaces you may need
#Stateless //Facade is a seesion bean so it can be #Stateless or #Statefull for basic CRUD it shoud be #Stateless
public class EntityFacade extends AbstractFacade<MyEntity> {
#PersistenceContext(unitName = "MyPersistanceUnit") //remember to define it first
private EntityManager em;
#Override
protected EntityManager getEntityManager() {
return em;
}
Now u can define any methods that work with your Entity class. Remember if you expose your entity via Remote Interfaces it will be in a detached state. So after updating you will first nee to use the merge(object) method of EntityManager
EDIT
Abstract facade is a concept that is used with JPA Entity, NetBeans in version 7.3 generates it for you automatically. It is used to define the most common operations on Entities so you dont have to repeat the code for every Entity. It goes like this
public abstract class AbstractFacade<T> {
private Class<T> entityClass;
public AbstractFacade(Class<T> entityClass) {
this.entityClass = entityClass;
}
protected abstract EntityManager getEntityManager();
public void create(T entity) {
getEntityManager().persist(entity);
}
public void edit(T entity) {
getEntityManager().merge(entity);
}
public void remove(T entity) {
getEntityManager().remove(getEntityManager().merge(entity));
}
public T find(Object id) {
return getEntityManager().find(entityClass, id);
}
...
}
The function above do some basic CRUD operations without much effort. So extending the facade gives you the ability to have does operations defined you could say out of the box. Of course this just for basic configurations the Entity facade can use many entities and do some business logic before it persists anything. So in your case it would go like this:
public class EntityFacade extends AbstractFacade<MyEntity> {
#PersistenceContext(unitName = "MyPersistanceUnit") //remember to define it first
private EntityManager em;
#Override
protected EntityManager getEntityManager() {
return em;
}
public doSomething(MyEntity entity)
{
entity.get(...);
...
entity.set(...)
if(iWantToPesristIt)
edit(entity)
else
return;
}
}

OpenEntityManagerInViewInterceptor analog for CLI application?

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.

Categories

Resources