Supposing the following code snippet which uses #PrePersist and #PreUpdate annotations and Joined-type inheritance:
#Entity
#Inheritance(strategy=InheritanceType.JOINED)
public abstract class A {
...
#PrePersist
private void prePersist() {
...
}
#PreUpdate
private void preUpdate() {
...
}
}
#Entity
#DiscriminatorValue("B")
public class B extends A {
...
#PrePersist
private void prePersist() {
...
}
#PreUpdate
private void preUpdate() {
...
}
}
Question: Can we rely on any order of execution of the callback methods?
For example when persisting class A and B will the prePersist method in B executed before the prePersist method in A or viceversa?
Can we assume that the prePersist in B will be executed before the class A is persisted?
Yes. First the superclass callbacks will be executed.
When an event is raised, the listeners are executed in this order:
#EntityListeners for a given entity or superclass in the array order
Entity listeners for the superclasses (highest first)
Entity Listeners for the entity
Callbacks of the superclasses (highest first)
Callbacks of the entity
For more details details read about: "Callbacks and listeners inheritance" at
https://docs.jboss.org/hibernate/entitymanager/3.5/reference/en/html/listeners.html
For me the solution was use the annotation
Example:
import lombok.Data;
import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
import javax.persistence.PrePersist;
import java.time.LocalDateTime;
#Data
#MappedSuperclass
public abstract class AuditObject {
#Column(name = "dat_created")
private LocalDateTime createdAt;
#PrePersist
public void onPrePersist() {
this.createdAt = LocalDateTime.now();
}
}
#Entity
public class Person extends AuditObject {
}
Related
Is it by any means possible to #Inject a Service-Bean (say a session bean) into an entity Listener?
Consider the following scenario as an example
Entity:
#Entity
#EntityListeners(BookListener.class)
public class Book {
// fields, getters & setters
}
Utility class:
#Singleton
public class BookUtil {
private BookRepository bookRepo;
private List<Book> bookList;
#Inject
public BookUtil(BookRepository bookRepo){
this.bookRepo = bookRepo;
this.bookList = this.bookRepo.findAll();
}
public void refreshBooks(){
this.bookList = this.bookRepo.findAll();
}
}
Listener:
public class BookListener {
#Inject
BookUtil bookUtil // --> CAN THIS BE ACHIEVED?
#PostPersist
private void refreshCache(Book b){
bookUtil.refreshBooks();
}
}
I tried out several things I could think of but none of them successfully injected an instance of BookUtil. I could manually instantiate it, which works. But I prefer injection as then the BookRepository(inside the BookUtil) would also be injected, without me having to worry about it
dataNucleus 5.1.1: dnSetid NoSuchMethodError
#MappedSuperclass
public class Foo {
#Transient
public Long getId() {
...
}
public void setId(Long id) {
...
}
}
#Entity
public class Bar extends Foo {
#Id
#GeneratedValue(strategy=GenerationType.TABLE, generator="gen")
#TableGenerator(name="gen", ...)
public Long getId() {
...
}
}
java.lang.NoSuchMethodError: No virtual method dnSetid(Long) in class com.example.Bar
at com.example.Bar.dnCopyKeyFieldsFromObjectId(Unknown Source:15)
at com.example.Bar.dnNewInstance(Unknown Source:10)
at org.datanucleus.enhancer.EnhancementHelper.newInstance(EnhancementHelper.java:178)
at org.datanucleus.state.StateManagerImpl.initialiseForHollow(StateManagerImpl.java:373)
at org.datanucleus.state.ObjectProviderFactoryImpl.newForHollow(ObjectProviderFactoryImpl.java:113)
at org.datanucleus.ExecutionContextImpl.findObject(ExecutionContextImpl.java:3194)
at org.datanucleus.store.rdbms.query.PersistentClassROF.findObjectWithIdAndLoadFields(PersistentClassROF.java:458)
at org.datanucleus.store.rdbms.query.PersistentClassROF.getObject(PersistentClassROF.java:364)
at org.datanucleus.store.rdbms.query.ForwardQueryResult.nextResultSetElement(ForwardQueryResult.java:180)
at org.datanucleus.store.rdbms.query.ForwardQueryResult$QueryResultIterator.next(ForwardQueryResult.java:408)
at org.datanucleus.store.rdbms.query.ForwardQueryResult.processNumberOfResults(ForwardQueryResult.java:136)
at org.datanucleus.store.rdbms.query.ForwardQueryResult.advanceToEndOfResultSet(ForwardQueryResult.java:164)
at org.datanucleus.store.rdbms.query.ForwardQueryResult.closingConnection(ForwardQueryResult.java:290)
at org.datanucleus.store.query.AbstractQueryResult.disconnect(AbstractQueryResult.java:105)
at org.datanucleus.store.rdbms.query.AbstractRDBMSQueryResult.disconnect(AbstractRDBMSQueryResult.java:251)
at org.datanucleus.store.rdbms.query.JPQLQuery$2.managedConnectionPreClose(JPQLQuery.java:654)
at org.datanucleus.store.rdbms.ConnectionFactoryImpl$ManagedConnectionImpl.close(ConnectionFactoryImpl.java:532)
at org.datanucleus.store.connection.AbstractManagedConnection.release(AbstractManagedConnection.java:83)
at org.datanucleus.store.rdbms.ConnectionFactoryImpl$ManagedConnectionImpl.release(ConnectionFactoryImpl.java:371)
at org.datanucleus.store.rdbms.query.JPQLQuery.performExecute(JPQLQuery.java:730)
at org.datanucleus.store.query.Query.executeQuery(Query.java:1966)
at org.datanucleus.store.query.Query.executeWithMap(Query.java:1873)
dnSetid should be added by enhancement. Decompiled Bar.class: there is no such method. It contains dnGetid() and other methods dn****.
The only way of overriding is to override BOTH getter AND setter.
Likely the enhancer relies on both being there, whether it is for a defined property or an overridden property.
I've two entities Person, Employee and Employee1. I want to implement entities inheritance in Spring Data MongoDB. Like in Spring Data JPA, what are the equivalent annotations for #Inheritance and #PrimaryKeyJoinColumn in Spring Data MongoDB. Right now, I've implemented something like this:
interface Person {
String getId();
void setId(String id);
String getName();
void getName(String name);
}
#Document(collection = "person")
class PersonImpl implements Person {
#Id
String id;
// Getters and setters
// Constructors, equals, hashcode and toString methods
}
interface Employee extends Person {
int getNumberOfDependents();
void getNumberOfDependents(int numberOfDependents);
}
#Document(collection = "employee")
class EmployeeImpl extends PersonImpl implements Employee {
// Getters and setters
// Constructors, equals, hashcode and toString methods
}
interface Employee1 extends Person {
int getNumberOfDependents();
void getNumberOfDependents(int numberOfDependents);
}
#Document(collection = "employee1")
class Employee1Impl extends PersonImpl implements Employee1 {
// Getters and setters
// Constructors, equals, hashcode and toString methods
}
Repository structure:
public interface PersonRepository extends MongoRepository<Person, String> {
}
public interface EmployeeRepository extends MongoRepository<Employee, String> {
}
public interface Employee1Repository extends MongoRepository<Employee1, String> {
}
I'm saving the Person object first and then taking the ID of it and creating an Employee object with the same ID and saving it. This creates new object and hence I'm losing all the Person object stuff.
I also feel that I've to get the NoRepositoryBean implemented also.
I'm confused. Please help.
Here is one approach:
#Document(collection = "person")
class Person {
#Id
String id;
// Getters and setters
// Constructors, equals, hashcode and toString methods
}
Note that the collection field refers to "person" and not to "employee"
#Document(collection = "person")
class Employee extends Person {
String jobTitle;
// Getters and setters
// Constructors, equals, hashcode and toString methods
}
In this method you do not need to create a repository for each derived class
#Repository
public interface PersonRepository extends MongoRepository<Person, String> {}
Code example:
#Autowired
private PersonRepository personRepo;
public void test() {
Employee employee = new Employee("1", "teacher")
personRepo.save(employee)
Optional<Person> optionalPerson = personRepo.findById("1");
Employee employeeFromDb;
if (optionalPerson.isPresent()) {
employeeFromDb = (Employee)optionalPerson.get()
}
else {
// could not find in db
}
}
if you want to find all employees you should have a methode on MongoRepository
called
List<Employee> findAll();
Here is the unit testing code. When we run unit test code (SampleServiceTest2); EntityManager injected in AbstractDao is always null! How can we inject em during unit test.
*** SampleServiceTest2.java
import javax.inject.Inject;
import org.jglue.cdiunit.CdiRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
#RunWith(CdiRunner.class)
public class SampleServiceTest2 {
#Inject SampleService greeter;
#Test
public void testGreeter() throws Exception {
System.out.println("before2");
greeter.addSampleData(new SampleDataDto(), new KullaniciDto());
System.out.println("after2");
}
}
*** SampleService.java
import javax.ejb.Stateless;
import javax.inject.Inject;
....
#Stateless
#SecuredBean
public class SampleService {
#Inject
SampleLogic sampleLogic;
#Yetki(tag="perm_add_sample_data")
public void addSampleData(SampleDataDto data, KullaniciDto aktifKullaniciDto){
SampleDataHelper sampleDataHelper = new SampleDataHelper();
SampleData sampleData = sampleDataHelper.getEntity(data);
KullaniciHelper kullaniciHelper = new KullaniciHelper();
Kullanici kullanici = kullaniciHelper.getEntity(aktifKullaniciDto);
sampleLogic.addData(sampleData, kullanici);
}
}
**** SampleLogic.java
import javax.inject.Inject;
....
public class SampleLogic {
#Inject
SampleDataDao sampleDataDao;
public void addData(SampleData data, Kullanici kullanici) {
addData1(data,kullanici);
System.out.println("SampleLogic : addData() called!");
}
public void addData1(SampleData data, Kullanici kullanici) {
sampleDataDao.create(data, kullanici);
}
}
**** SampleDataDao.java
public class SampleDataDao extends AbstractDao<SampleData> {
private static final long serialVersionUID = 1L;
}
**** AbstractDao.java
public abstract class AbstractDao<T extends BaseEntity> implements Serializable {
private static final long serialVersionUID = 1L;
#PersistenceContext(unitName="meopdb")
private EntityManager em;
protected EntityManager getEm() {
return em;
}
#SuppressWarnings("rawtypes")
private Class entityClass;
#SuppressWarnings("rawtypes")
private Class getEntityClass() {
if (entityClass == null) {
entityClass = (Class) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
return entityClass;
}
public T create(T t, Kullanici kullanici) {
if (t.getId() != null) {
throw new IllegalStateException("Create Operation: Oid should be null");
}
t.setId(getSeqNextValue(t));
t.setEklemeZamani(new Timestamp(Calendar.getInstance().getTimeInMillis()));
t.setEkleyenKullaniciId(kullanici.getId());
t.setDurumId(EnumDurum.AKTIF.getValue());
t = em.merge(t);
em.flush();
return t;
}
}
If you test with CDIUnit, the only thing you get is CDI injections, not the full power of Java EE. Injecting entityManager using #PersistenceContext into AbstractDAO is not part of standalone CDI, it is only supported when application is running within a Java EE application server.
The solution is to inject EntityManager using CDI mechanism and create a producer. The producer could be then switched for an alternative in unit tests to provide test entityManager. However, setting up JPA in a standalone unit test is not so straightforward, as you need to specify connection properties directly in persistence.xml file. Also, do not forget to add dependencies on a JPA implementation (hibernate, eclipselink) into your test dependencies.
However, if you do not want to adapt your application's code or you need more than CDI in your tests, you should have a look at Arquillian Java EE test framework.
Here is an example for CDIUnit:
public abstract class AbstractDao<T extends BaseEntity> implements Serializable {
...
#Inject
#Named("meopdb")
private EntityManager em;
...
}
// producer in application - just a wraper over `#PersisteneContext`
public class EntityManagerProducer {
#Produces
#PersistenceContext(unitName="meopdb")
#Named("meopdb")
private EntityManager em;
}
/* producer in your test sources - it creates entityManager via API calls instead of injecting via `#PersistenceContext`. Also, a different persistence unit is used so that it does not clash with main persistence unit, which requires datasource from app server
*/
public TestEntityManagerProducer {
#Produces
#ProducesAlternative // CDIUnit annotation to turn this on as an alternative automatically
#Named("meopdb")
public EntityManager getEm() {
return Persistence
.createEntityManagerFactory("meopdb-test")
.createEntityManager();
}
}
And it is not yet enough. You need to create a new persistence.xml in your test resources with the test persistence unit named "meopdb-test". For this unit you need to specify RESOURCE_LOCAL transaction-type, and specify connection information. And last thing not to forget - you need to list all your entities in the persistence.xml, or in external orm file. This is because your tests run outside of application server. Inside app server, JPA can find entities automatically.
As #OndroMih said, in CDI-Unit, the only thing you get is CDI injections. So you have to cheat a little.
You can use extension do add javax.inject.Inject annnotation to all #PersistenceContext injections
import java.util.Set;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.spi.*;
import javax.inject.Inject;
import javax.persistence.PersistenceContext;
import org.apache.deltaspike.core.util.metadata.AnnotationInstanceProvider;
import org.apache.deltaspike.core.util.metadata.builder.AnnotatedTypeBuilder;
public class AddInjectToPersistenceContextInjectionsCdiExtension implements Extension {
<T> void processAnnotatedType(#Observes ProcessAnnotatedType<T> pat) {
Set<AnnotatedField<? super T>> fields = pat.getAnnotatedType().getFields();
for (AnnotatedField<? super T> field : fields) {
if (shouldInjectionAnnotationBeAddedToField(field)) {
AnnotatedType<T> at = pat.getAnnotatedType();
AnnotatedTypeBuilder<T> builder = new AnnotatedTypeBuilder<T>().readFromType(at);
Inject injectAnnotation = AnnotationInstanceProvider.of(Inject.class);
builder.addToField(field, injectAnnotation);
pat.setAnnotatedType(builder.create());
}
}
}
private <X> boolean shouldInjectionAnnotationBeAddedToField(AnnotatedField<? super X> field) {
return !field.isAnnotationPresent(Inject.class) &&
field.isAnnotationPresent(PersistenceContext.class);
}
}
and produce suitable EntityManager in test class
#RunWith(CdiRunner.class)
#AdditionalClasses(AddInjectToPersistenceContextInjectionsCdiExtension.class)
public class SampleServiceTest2 {
#Inject SampleService greeter;
EntityManagerFactory emf;
#PostConstruct
void init() {
emf = Persistence.createEntityManagerFactory("integration");
}
#Produces
EntityManager createEntityManager() {
return emf.createEntityManager();
}
#Test
public void testGreeter() throws Exception {
}
}
It's not exactly equivalent of what Java EE container does, but it's close enough more often than not.
Can I define same JPA callback method in parent and child class as below? If yes, do I need to invoke super.onPrePersist(); in child class onPrePersist() method?
#MappedSuperclass
public abstract class AbstractEntity {
#PrePersist
protected onPrePersist() {
System.out.println("Parent onPrePersist() invoked");
}
}
#Entity
#Table(name = "child")
public class Child extends AbstractEntity {
#PrePersist
protected onPrePersist() {
**super.onPrePersist();**
System.out.println("Child onPrePersist() invoked");
}
}
I have written a unit test for the above scenario and It works. For each of the callback methods in child class, you have to invoke the parent callback method first:
#Override
#PrePersist
protected onPrePersist() {
**super.onPrePersist();**
System.out.println("Child onPrePersist() invoked");
}
You don't have to invoke the parent callback method by yourself, just don't override the #PrePersist annotated method as it hides the parent method and prevents it from being executed. If your callback methods have different names they will be invoked in the order according to their place in the hierarchy, most general classes first.
#MappedSuperclass
public abstract class AbstractEntity {
#PrePersist
protected onPrePersistParent() {
System.out.println("Parent onPrePersist() invoked");
}
}
#Entity
#Table(name = "child")
public class Child extends AbstractEntity {
#PrePersist
protected onPrePersistChild() {
System.out.println("Child onPrePersist() invoked");
}
}
This will produce the output:
Parent onPrePersist() invoked
Child onPrePersist() invoked