I was going through the hibernate documentation and the document says that hibernate requires a no-arg constructor for all our persistent classes:
The no-argument constructor is a requirement for all persistent
classes; Hibernate has to create objects for you, using Java
Reflection. The constructor can be private, however package or public
visibility is required for runtime proxy generation and efficient data
retrieval without bytecode instrumentation.
But when I created a sample program for testing by creating a POJO class without any no-arg constructor and by placing a constructor that takes parameters, I expected Hibernate will throw an exception but I was surprised to see that I was not getting exceptions.
Here is my POJO:
public class Event {
private Long id;
private String title;
public Event(String title) {}
public Long getId() {
return id;
}
private void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
Here is my program:
import org.hibernate.Session;
import java.util.*;
import org.hibernate.tutorial.domain.Event;
import org.hibernate.tutorial.util.HibernateUtil;
public class EventManager {
public static void main(String[] args) {
EventManager mgr = new EventManager();
mgr.createAndStoreEvent("My Event", new Date());
HibernateUtil.getSessionFactory().close();
}
private void createAndStoreEvent(String title, Date theDate) {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
Event theEvent = new Event("");
theEvent.setTitle(title);
theEvent.setDate(theDate);
session.save(theEvent);
session.getTransaction().commit();
}
}
I have also configured hibernate configuration and mapping files. Now I can see that a new record is stored in my DB when I execute this program.
I am new to Hibernate, please help me in understanding the statement given in hibernate documentation as the document says we need a no-arg constructor. I am using Hibernate 3.x version.
Try to read your event and you will see the exception.
The problem is not in saving object in database when object is already created by explicit call of the constructor. The problem is when hibernate reads record from DB and has to create corresponding object of mapped class. In this case it should create object. How can it do it without default constructor? Indeed there can be several constructor with arguments and it "does not know" which one to take.
So it does something like this:
Object obj = Class.forName(mappedClassName).newInstance();
pupulateProperties(obj);
newInstance() requires no-args constructor.
You try this one then you will get the exception.
Event event = (Event)session.get(Event.class, eventId );
Add default constructor in your event class
public class Event {
public Event(){
}
}
Related
I have a JPA Project (Eclipse Link), works fine but I want to persist a class that is not Entity(Or Not entity in the same Persistence Context), currently I persist the reference id, and after that I do the call to retrieve the Object. I need know what is the best way to do that.. I do not want add code in the bean as listener event, because I want have a clean bean(constructos,properties, setters and getters without annotations),
My Idea is to extend the PersistenceContext(but, I do not know how to do it) adding a filter and identify the class to persist and doing something to replace the persistence of the class not mapped.
Any ideas or my question is out of place?
This is a Simple Example..
#Entity
public class Customer{
#Column
Integer id;
#Column
/*transient?*/
CustomerInfo customerInfo
/*setters and getters*/
}
/*this class maybe not be Entity.. Maybe be a Web Service Response Bean*/
public class CustomerInfo{
private String firstName;
private String lastName;
private BigDecimal balance;
/*setters and getters*/
}
Following my comments: create an embeddable CustomerInfoKey with the essential information.
Two solutions then:
Inheritance:
Have CustomerInfo inherits CustomerInfoKey
Use setCustomerInfoKey(customerInfo) with customerInfo a CustomerInfo.
Composition and delegation:
Have a field CustomerInfoKey key in CustomerInfo
Delegate the getter/setter of CustomerInfoKey in CustomerInfo:
public Foobar getFoobar() {return key.getFoobar()}
public void setFoobar(Foobar foobar) {key.setFoobar(key);}
Have a method getKey() and use it to persist data; you can even create a setter taking a CustomerInfo in Customer and doing the appropriate stuff.
I don't know how JPA implementations behave when it encounters partial mapping like solution #1. But it should work.
As proposed by NoDataFound in the comment, if you do not want to add an Id, an Embeddable/Embedded tandem could be the solution: because of the Id problem, you should have the data in the same table (it is possible to keep different classes). You have the doc in the Java EE tutorial. If you don't want to change the code, you could use the XML for object/relational mapping. In the wikibook about JPA you have an XML sample.
To resolve this, I am creating a EntityListener:
public interface JREntityListener<T> {
public Class getTarget();
public void postUpdate(T t) throws Exception;
public void postCreate(T t) throws Exception;
public void preMerge(T t) throws Exception;
public void postMerge(T t) throws Exception;
public void prePersist(T t) throws Exception;
public void postPersist(T t) throws Exception;
}
I am Created a Class to Catch the events of a Entity
public class JRDescriptorEventListener<T> implements DescriptorEventListener{
//implements all methods of DescriptorEventListener
//i am Show only One to Example
#Override
public void postClone(DescriptorEvent descriptorEvent) {
// descriptorEvent.getObject();
try {
logger.info("postClone");
t.postUpdate((T) descriptorEvent.getObject());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
I am created a Binding EntityListener with PersistenceContext (Thanks jhadley by injecting a spring dependency into a JPA EntityListener):
public void addListener(JREntityListener t) {
JpaEntityManager entityManager = null;
try {
// Create an entity manager for use in this function
entityManager = (JpaEntityManager) entityManagerFactory.createEntityManager();
// Use the entity manager to get a ClassDescriptor for the Entity class
ClassDescriptor desc =
entityManager.getSession().getClassDescriptor(t.getTarget());
JRDescriptorEventListener jrDescriptorEventListener = new JRDescriptorEventListener(t);
desc.getEventManager().addListener(jrDescriptorEventListener);
logger.info("Entity Listener for " + t.getTarget().getCanonicalName() + " is added");
} catch (Exception e) {
logger.error(e.getMessage(), e);
} finally {
if (entityManager != null) {
// Cleanup the entity manager
entityManager.close();
}
}
}
Now I am implements the Listener addind Listener
javoraiPersistenceBase.addListener(myEntityListener);
public class MyEntityListener implements JavoraiEntityListener<Customer> {
#Autowired
CustomerSvc customerSvc;
#Override
public Class getTarget() {
return Customer.class;
}
#Override
public void postUpdate(Customer customer) throws Exception {
CustomerInfo customerInfo = globalDataSvc.findCustomerInfoById(customer.getCustomerInfo().getId());
customer.setCustomerInfo(customerInfo);
}
}
I'm trying to get the old entity in a #HandleBeforeSave event.
#Component
#RepositoryEventHandler(Customer.class)
public class CustomerEventHandler {
private CustomerRepository customerRepository;
#Autowired
public CustomerEventHandler(CustomerRepository customerRepository) {
this.customerRepository = customerRepository;
}
#HandleBeforeSave
public void handleBeforeSave(Customer customer) {
System.out.println("handleBeforeSave :: customer.id = " + customer.getId());
System.out.println("handleBeforeSave :: new customer.name = " + customer.getName());
Customer old = customerRepository.findOne(customer.getId());
System.out.println("handleBeforeSave :: new customer.name = " + customer.getName());
System.out.println("handleBeforeSave :: old customer.name = " + old.getName());
}
}
In the event I try to get the old entity using the findOne method but this return the new event. Probably because of Hibernate/Repository caching in the current session.
Is there a way to get the old entity?
I need this to determine if a given property is changed or not. In case the property is changes I need to perform some action.
If using Hibernate, you could simply detach the new version from the session and load the old version:
#RepositoryEventHandler
#Component
public class PersonEventHandler {
#PersistenceContext
private EntityManager entityManager;
#HandleBeforeSave
public void handlePersonSave(Person newPerson) {
entityManager.detach(newPerson);
Person currentPerson = personRepository.findOne(newPerson.getId());
if (!newPerson.getName().equals(currentPerson.getName)) {
//react on name change
}
}
}
Thanks Marcel Overdijk, for creating the ticket -> https://jira.spring.io/browse/DATAREST-373
I saw the other workarounds for this issue and want to contribute my workaround as well, cause I think it´s quite simple to implement.
First, set a transient flag in your domain model (e.g. Account):
#JsonIgnore
#Transient
private boolean passwordReset;
#JsonIgnore
public boolean isPasswordReset() {
return passwordReset;
}
#JsonProperty
public void setPasswordReset(boolean passwordReset) {
this.passwordReset = passwordReset;
}
Second, check the flag in your EventHandler:
#Component
#RepositoryEventHandler
public class AccountRepositoryEventHandler {
#Resource
private PasswordEncoder passwordEncoder;
#HandleBeforeSave
public void onResetPassword(Account account) {
if (account.isPasswordReset()) {
account.setPassword(encodePassword(account.getPassword()));
}
}
private String encodePassword(String plainPassword) {
return passwordEncoder.encode(plainPassword);
}
}
Note: For this solution you need to send an additionally resetPassword = true parameter!
For me, I´m sending a HTTP PATCH to my resource endpoint with the following request payload:
{
"passwordReset": true,
"password": "someNewSecurePassword"
}
You're currently using a spring-data abstraction over hibernate.
If the find returns the new values, spring-data has apparently already attached the object to the hibernate session.
I think you have three options:
Fetch the object in a separate session/transaction before the current season is flushed. This is awkward and requires very subtle configuration.
Fetch the previous version before spring attached the new object. This is quite doable. You could do it in the service layer before handing the object to the repository. You can, however not save an object too an hibernate session when another infect with the same type and id it's known to our. Use merge or evict in that case.
Use a lower level hibernate interceptor as described here. As you see the onFlushDirty has both values as parameters. Take note though, that hibernate normally does not query for previous state of you simply save an already persisted entity. In stead a simple update is issued in the db (no select). You can force the select by configuring select-before-update on your entity.
Create following and extend your entities with it:
#MappedSuperclass
public class OEntity<T> {
#Transient
T originalObj;
#Transient
public T getOriginalObj(){
return this.originalObj;
}
#PostLoad
public void onLoad(){
ObjectMapper mapper = new ObjectMapper();
try {
String serialized = mapper.writeValueAsString(this);
this.originalObj = (T) mapper.readValue(serialized, this.getClass());
} catch (Exception e) {
e.printStackTrace();
}
}
}
I had exactly this need and resolved adding a transient field to the entity to keep the old value, and modifying the setter method to store the previous value in the transient field.
Since json deserializing uses setter methods to map rest data to the entity, in the RepositoryEventHandler I will check the transient field to track changes.
#Column(name="STATUS")
private FundStatus status;
#JsonIgnore
private transient FundStatus oldStatus;
public FundStatus getStatus() {
return status;
}
public FundStatus getOldStatus() {
return this.oldStatus;
}
public void setStatus(FundStatus status) {
this.oldStatus = this.status;
this.status = status;
}
from application logs:
2017-11-23 10:17:56,715 CompartmentRepositoryEventHandler - beforeSave begin
CompartmentEntity [status=ACTIVE, oldStatus=CREATED]
Spring Data Rest can't and likely won't ever be able to do this due to where the events are fired from. If you're using Hibernate you can use Hibernate spi events and event listeners to do this, you can implement PreUpdateEventListener and then register your class with the EventListenerRegistry in the sessionFactory. I created a small spring library to handle all of the setup for you.
https://github.com/teastman/spring-data-hibernate-event
If you're using Spring Boot, the gist of it works like this, add the dependency:
<dependency>
<groupId>io.github.teastman</groupId>
<artifactId>spring-data-hibernate-event</artifactId>
<version>1.0.0</version>
</dependency>
Then add the annotation #HibernateEventListener to any method where the first parameter is the entity you want to listen to, and the second parameter is the Hibernate event that you want to listen for. I've also added the static util function getPropertyIndex to more easily get access to the specific property you want to check, but you can also just look at the raw Hibernate event.
#HibernateEventListener
public void onUpdate(MyEntity entity, PreUpdateEvent event) {
int index = getPropertyIndex(event, "name");
if (event.getOldState()[index] != event.getState()[index]) {
// The name changed.
}
}
Just another solution using model:
public class Customer {
#JsonIgnore
private String name;
#JsonIgnore
#Transient
private String newName;
public void setName(String name){
this.name = name;
}
#JsonProperty("name")
public void setNewName(String newName){
this.newName = newName;
}
#JsonProperty
public void getName(String name){
return name;
}
public void getNewName(String newName){
return newName;
}
}
Alternative to consider. Might be reasonable if you need some special handling for this use-case then treat it separately. Do not allow direct property writing on the object. Create a separate endpoint with a custom controller to rename customer.
Example request:
POST /customers/{id}/identity
{
"name": "New name"
}
I had the same problem, but I wanted the old entity available in the save(S entity) method of a REST repository implementation (Spring Data REST).
What I did was to load the old entity using a 'clean' entity manager from which I create my QueryDSL query:
#Override
#Transactional
public <S extends Entity> S save(S entity) {
EntityManager cleanEM = entityManager.getEntityManagerFactory().createEntityManager();
JPAQuery<AccessControl> query = new JPAQuery<AccessControl>(cleanEM);
//here do what I need with the query which can retrieve all old values
cleanEM.close();
return super.save(entity);
}
The following worked for me. Without starting a new thread the hibernate session will provide the already updated version. Starting another thread is a way to have a separate JPA session.
#PreUpdate
Thread.start {
if (entity instanceof MyEntity) {
entity.previous = myEntityCrudRepository.findById(entity?.id).get()
}
}.join()
Just let me know if anybody would like more context.
Don't know if you're still after an answer, and this is probably a bit 'hacky', but you could form a query with an EntityManager and fetch the object that way ...
#Autowired
EntityManager em;
#HandleBeforeSave
public void handleBeforeSave(Customer obj) {
Query q = em.createQuery("SELECT a FROM CustomerRepository a WHERE a.id=" + obj.getId());
Customer ret = q.getSingleResult();
// ret should contain the 'before' object...
}
My client application is a standalone java application using Netbeans RCP. The client application connect to a remote EJB mounted on a Glassfish server.
On the server side, i have a business method (i made for testing) that's supposed to take an instance of an entity (this entity is not persisted or managed yet), persist it (using the persit of the EntityManager).
Here is how this method looks like:
#Override
public TestLinkEntity test(TestLinkEntity c) {
em.persist(c);
return c;
}
Called from the client side like this:
TestLinkEntity c = remote.test(new TestLinkEntity());
Here is the TestLinkEntity declaration:
#Entity
public class TestLinkEntity implements AltEntity, Serializable {
private Set<TestAppEntity> links = new HashSet<TestAppEntity>();
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Override
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#OneToMany(mappedBy="links")
public Set<TestAppEntity> getLinks() {
return links;
}
}
And now the TestAppEntity used in the one to many relationship.
#Entity
public class TestAppEntity implements Serializable, AltEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String test;
public TestAppEntity() {
}
#Override
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTest() {
return test;
}
public void setTest(String test) {
this.test = test;
}
}
The problem i'm facing is when i try to call the business method from the client (as shown above) i get a huge and quite unreadable exception on client side and only on client side. No exception logged on the Glassfish logs and the entity (TestLinkEntity)and potential links (TestAppEntity) are stored in the data base.
I pasted the exception here.
Here are few things i have noticed.
The exception only happen when i try to return a managed entity. If i replace the em.persist() by em.merge and do not return the new entity returned by merge for example, the exception will never be raised. e.g do something like:
#Override
public TestLinkEntity test(TestLinkEntity c) {
em.merge(c);
return c;
}
The exception only happen when the entity returned contains the one to many relationship. e.g something like the following code snippet will not raise an exception:
#Override
public TestAppEntity test(TestAppEntity c) {
em.persist(c);
return c;
}
TestAppEntity does not contain any relationship.
Things i'm sure:
TestAppEntity and TestLinkEntity are the same on both client and server sides.
EDIT:
Due to the answer of #James, i'm now wondering what is the difference between the package javax.persistence in eclispelink.jar and in javaee.jar. Should i include both?
Including both cause troubles (maybe due to the fact that package name in both jars are the same).
error: in class file javax/persistence/NamedQuery.class(javax/persistence:NamedQuery.class): unknown enum constant javax.persistence.LockModeType.NONE
Note: Attempting to workaround javac bug #6512707
warning: No processor claimed any of these annotations: [javax.ejb.Remote]
error: in class file j javax/persistence/NamedQuery.class(javax/persistence:NamedQuery.class): unknown enum constant javax.persistence.LockModeType.NONE
It seems to be some bug in the CORBA serialization you are using.
My guess is you don't have the eclipseLink.jar on your client, but you need it. As objects read from the database will contains special LAZY collections instances.
I'm migrating a legacy system over to use Hibernate 3. It currently generates its own identifiers. To keep with what the system currently does before I try and move it over to something a little better, how would I go about specifying (using annotations) my own class that will return the custom generated identifiers when an insert occurs?
Something like:
#Id
#CustomIdGenerator(Foo.class) // obviously this is not a real annotation
public String getId() { ... }
Where the Foo class has one method that generates the identifier.
Currently I'm just calling the setId(String id) method manually but was hoping for a better way to deal with this situation.
I don't think there is out-of-box support for generating custom Ids using custom annotations using pure JPA-2 API. But if you want to use provider specific API, then the job is pretty simple. Sample Example
To be provider independent try any of following tricks....
IdGeneratorHolder
public abstract class IdGeneratorHolder {
/* PersistentEntity is a marker interface */
public static IdGenerator getIdGenerator(Class<? extends PersistentEntity> entityType) {
/* sample impelementation */
if(Product.class.isAssignableFrom(entityType)) {
return new ProductIdGenerator();
}
return null;
}
}
General IdGenerator interface
public interface IdGenerator {
String generate();
}
Specific IdGenerator - Product Id Generator
public class ProductIdGenerator implements IdGenerator {
public String generate() {
/* some complicated logic goes here */
return ${generatedId};
}
}
Now set the generated id either in no-arg constructor OR in #PrePersist method.
Product.java
public class Product implements PersistentEntity {
private String id;
public Product() {
id = IdGeneratorHolder.getIdGenerator(getClass()).generate();
}
#PrePersist
public void generateId() {
id = IdGeneratorHolder.getIdGenerator(getClass()).generate();
}
}
In above example all the ids are of the same type i.e. java.lang.String. If the persistent entities have ids of different types.....
IdGenerator.java
public interface IdGenerator {
CustomId generate();
}
CustomId.java
public class CustomId {
private Object id;
public CustomId(Object id) {
this.id = id;
}
public String toString() {
return id.toString();
}
public Long toLong() {
return Long.valueOf(id.toString());
}
}
Item.java
#PrePersist
public void generateId() {
id = IdGeneratorHolder.getIdGenerator(getClass()).generate().toLong();
}
You can also use your custom annotation...
CustomIdGenerator.java
public #interface CustomIdGenerator {
IdStrategy strategy();
}
IdStrategy.java
enum IdStrategy {
uuid, humanReadable,
}
IdGeneratorHolder.java
public abstract class IdGeneratorHolder {
public static IdGenerator getIdGenerator(Class<? extends PersistentEntity> entityType) {
try { // again sample implementation
Method method = entityType.getMethod("idMethod");
CustomIdGenerator gen = method.getAnnotation(CustomIdGenerator.class);
IdStrategy strategy = gen.strategy();
return new ProductIdGenerator(strategy);
}
One more thing.... If we set id in #PrePersist method, the equals() method cannot rely on id field (i.e. surrogate key), we have to use business/natural key to implement equals() method. But if we set id field to some unique value (uuid or "app-uid" unique within application) in no-arg constructor, it helps us to implement the equals() method.
public boolean equals(Object obj) {
if(obj instanceof Product) {
Product that = (Product) obj;
return this.id ==that.id;
}
return false;
}
If we or someone else call (intentionally or by mistake) the #PrePersist annotated method more than one times, the "unique id will be changed!!!" So setting id in no-arg constructor is preferable. OR to address this issue put a not null check...
#PrePersist
public void generateId() {
if(id != null)
id = IdGeneratorHolder.getIdGenerator(getClass()).generate();
}
}
UPDATE
If we put the id generation in a
no-arg constructor, wouldn't that
cause a problem when loading entities
from the database? because hibernate
will call the no-arg constructor
causing existing ids to be
re-generated
Yeah you are right, I missed that part. :( Actually, I wanted to tell you that:- in my application every Entity object is associated with an Organization Entity; so I've created an abstract super class with two constructors, and every Entity (except Organization) extends this class.
protected PersistentEntityImpl() {
}
protected PersistentEntityImpl(Organization organization) {
String entityId = UUIDGenerator.generate();
String organizationId = organization.getEntityId();
identifier = new EntityIdentifier(entityId, organizationId);
}
The no-arg constructor is for JPA provider, we never invoke no-arg constructor, but the other organization based constructor. As you can see. id is assigned in Organization based constructor. (I really missed this point while writing the answer, sorry for that).
See if you can implement this or similar strategy in your application.
The second option was using the
#PrePersist annotation. I put that in
and the method never got hit and gave
me an exception stating that I needed
to set the id manually. Is there
something else I should be doing?
Ideally, JPA provider should invoke #PrePersist methods (one declared in class and also all the other methods that are declared in super-classes) before persisting the entity object. Can't tell you what is wrong, unless you show some code and console.
You can.
First, implement org.hibernate.id.IdentifierGenerator
Then you'd have to map it in a mapping xml file. I couldn't find a way to do this with annotations:
<!--
<identifier-generator.../> allows customized short-naming
of IdentifierGenerator implementations.
-->
<!ELEMENT identifier-generator EMPTY>
<!ATTLIST identifier-generator name CDATA #REQUIRED>
<!ATTLIST identifier-generator class CDATA #REQUIRED>
Finally, use #GeneratedValue(generator="identifier-name")
Note that this is hibernate-specific (not JPA)
Update: I took a look at the sources of Hibernate, and it seems at one place, after failing to resolve the short name, hibernates attempts to call Class.forName(..). The parameter there is called strategy. So Here's what you try:
try setting the class fully-qualified name as string in the generator attribute
try setting the class fqn as string in the #GenericGenerator strategy attribute (with some arbitrary name)
Let me know which (if any) worked
I'm looking for a way to persist an entity which contains a field of a user type.
In this particular example I would like to persist the ts field as number of milliseconds.
import org.joda.time.DateTime;
#Entity
public class Foo {
#Id
private Long id;
private DateTime ts;
}
JPA does not have the ability to register custom property types, you'll have to use provider specific stuff:
Hibernate: http://joda-time.sourceforge.net/contrib/hibernate/
EclipseLink: http://code.google.com/p/joda-time-eclipselink-integration/
OpenJPA: http://issues.apache.org/jira/browse/OPENJPA-473 (still open)
Since it's not a JPA defined supported type you rely on implementation specifics.
DataNucleus has a plugin for JodaTime that would allow your desired persistence.
Either you can use those provider specific stuffs or you can make use of #PostPersist, #PostUpdate, #PostLoad callback methods with a surrogate #Transient field.
http://www.java2s.com/Tutorial/Java/0355__JPA/EntityListenerPostLoad.htm will give you some idea.
Please feel comfortable to get in touch for any further clarification.
One solution is to use non-column properties and encapsulate them with getters/setters.
To tell JPA to use getters/setters instead of directly accesing private fields, you must annotate #Id on public Long getId() instead of private Long id. When doing this, just remember to use #Transient for every getter that does not correspond directly to a column.
The following example would create a Date column named myDate, while the application would have DateTime getTs() and setTs() methods available for it. (not sure about DateTime API, so please forgive minor errors :))
import org.joda.time.DateTime;
#Entity
public class Foo {
private Long id;
private DateTime ts;
#Id
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
// These should be accessed only by JPA, not by your application;
// hence they are marked as protected
protected Date getMyDate() { return ts == null ? null : ts.toDate(); }
protected void setMyDate(Date myDate) {
ts = myDate == null ? null : new DateTime(myDate);
}
// These are to be used by your application, but not by JPA;
// hence the getter is transient (if it's not, JPA will
// try to create a column for it)
#Transient
public DateTime getTs() { return ts; }
public void setTs(DateTime ts) { this.ts = ts; }
}