JPA - how to get auto generated id in constructor? - java

What I want to achieve to take auto generated ID, hash it and save it into other field in the class, but at the stage of creating object by constructor ID is not yet generated. Any workaround ideas?
#Entity
public class MyClass {
#Id
#Column(name="id")
#GeneratedValue(strategy=GenerationType.AUTO)
Long id;
String hashID;
MyClass(){
this.hashID = Utils.hashID(id);
}
//setters and getters
}
```

One way that I can think of is you can use an entity lifecycle callback event like #PostLoad which is called when the entity is loaded in the persistence context, and initialize your hashed field from the id.
E.g.
#Entity
public class MyClass {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
Long id;
String hashID;
#PostLoad
public void postLoad() {
// Here id is initialized
this.hashID = Utils.hashID(id);
}
}

Related

possible alternatives to #GeneratedValue with #EmbeddedId in JPA

My entity has a wrapped Identifier like this,
#Entity
public class Article {
#EmbeddedId
private ArticleId articleId;
....
}
#Embeddable
public class ArticleId implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name="id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
}
in my architecture, multiple application instances(it all same.) are connected to the same data source
so #GeneratedValue(strategy = GenerationType.IDENTITY)seems good
because even if instances A and B try to create Accountat the same time, Its Id is guaranteed by the database.
problem is that #GeneratedValue is only able to use with #Id annotation (#Id is not available for EmbeddedId)
PersistenceUnitUtil.getIdentifier(Object entity) could be a alternative? like this,
ArticleId articleId = ArticleRepository.nextIdentity();
I am not sure that whether it causes the race condition.
Could PersistenceUnitUtil.getIdentifier(Object entity) guarantee the unique id across the different application instance(JVM)? I don't think so.
In this situation, What alternative is possible?
One solution could be to use an #IdClass to get rid of the nested property and be able to generate the identifier (since nested properties are "assigned" and cannot be generated, so the call of PersistenceUnitUtil.getIdentifier(Object entity) would not help here). See e.g. here for a complete guide (also linked in the linked answer by #SternK from the comment)
An #IdClass could look like this:
public class ArticleId implements Serializable {
private Long id;
}
An entity could use it:
#Entity
#IdClass(ArticleId.class) // specified dependency
public class Article {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
// expose "typed" id:
public ArticleId getId() {
return new ArticleId(id);
}
}
Spring-Data #Repositorys also work with corresponding #IdClass objects, e.g.:
#Repository
public interface UserEntityRepository extends JpaRepository<Article, ArticleId> {
}
// would offer e.g. this method:
repository.findById(new ArticleId(123L));

JPA #Id on #OneToOne Entity with a Composite Key

I have an #Entity class, with an #Id annotation and a #OneToOne annotation on the same field. Usually this would not be a problem, but the entity class of the field with these annotations uses a composite key. This is causing more complications than I anticipated.
Here is the entity class that is posing the problem:
#Entity
public class ReportDetails implements Serializable {
#Id
#OneToOne
private MachineLine machineLine;
}
And here is the MachineLine entity class that is being used as an ID in ReportDetails:
#Entity
#IdClass(MachineLine.MachineLineKey.class)
public class MachineLine {
#Id
#ManyToOne
private Machine machine;
#Id
private long lineIndex;
public static class MachineLineKey implements Serializable {
private Machine machine;
private long lineIndex;
}
}
I have left out any extra fields and the getters and setters from these class definitions, to save space.
When I try to run my application it gives the following exception:
java.lang.IllegalArgumentException: This class [class ReportDetails] does not define an IdClass
When I put an #IdClass annotation on ReportDetails it then requires defining the individual fields of whatever class I define in #IdClass, like in MachineLine. However, I am trying to avoid doing this, in favour of having the whole MachineLine entity returned whenever a ReportDetails entity is retrieved from the database.
Is there a way of having MachineLine as the ID field of ReportDetails, without having to define extra fields within ReportDetails?
This is what JPA calls a "derived identity". You might try something like this:
ReportDetails:
#Entity
public class ReportDetails implements Serializable {
// all attributes map by the relationship: AttributeOverride is not allowed
#EmbeddedId
private MachineLine.Id id;
#MapsId
#JoinColumns({
#JoinColumn(name="machineId", referencedColumnName="machineId"),
#JoinColumn(name="machineLineIndex", referencedColumnName="index")
})
#OneToOne
private MachineLine machineLine;
// ...
}
MachineLine:
#Entity
public class MachineLine {
#EmbeddedId
private Id id;
#MapsId("machineId") // maps machineId attribute of embedded id
#ManyToOne
private Machine machine;
// ...
#Embeddable
public static class Id implements Serializable {
private long machineId; // corresponds to PK type of Machine
private long index;
// ...
}
}
Machine:
#Entity
public class Machine {
#Id
private long id;
#OneToMany(mappedBy = "machine")
private List<MachineLine> lines;
// ...
}
Derived identities are discussed (with examples) in the JPA 2.2 spec in section 2.4.1.

How do I retrieve only the ID instead of the Entity of an association?

I have a class that looks something like this:
#Entity
public class EdgeInnovation {
#Id
public long id;
#ManyToOne
public NodeInnovation destination;
#ManyToOne
public NodeInnovation origin;
}
and another one that looks something like this:
#Entity
public class NodeInnovation {
#Id
public long id;
#OneToOne
public EdgeInnovation replacedEdge;
}
and so each table map to the other, so one entity will refer to other entities that will refer to more entities and so on, so that in the end there will be many entities that will be fetched from the database. Is there any way to only get the value (integer/long) of the key and not the entity it refers to? something like this:
#ManyToOne(referToThisTable="NodeInnovation")
#Entity
public class EdgeInnovation {
#Id
public long id;
#ManyToOne(referToTable="NodeInnovation")
public Long destination;
#ManyToOne(referToTable="NodeInnovation")
public Long origin;
}
and
#Entity
public class NodeInnovation {
#Id
public long id;
#OneToOne(referToTable="EdgeInnovation")
public Long replacedEdge;
}
Here's an example. I want the stuff in green, I get all the stuff in red along with it. This wastes memory and time reading from disk.
You would just map the foreign keys as basic mappings instead of Relationships:
#Entity
public class EdgeInnovation {
#Id
public long id;
#Column(name="DESTINATION_ID")
public Long destination;
#Column(name="ORIGIN_ID")
public Long origin;
}
Or you can have access to both the ID and the referenced entity within EdgeInnovation, but you'll need to decide which you want to use to set the mapping:
#Entity
public class EdgeInnovation {
#Id
public long id;
#Column(name="DESTINATION_ID", updatable=false, insertable=false)
public Long destination_id;
#ManyToOne
public NodeInnovation destination;
#Column(name="ORIGIN_ID", updatable=false, insertable=false)
public Long origin_id;
#ManyToOne
public NodeInnovation origin;
}
In the above example, the origin_id is read-only while the origin reference is used to set the foreign key in the table. Any changes though should be made to both fields to keep the object mappings in synch with each other.
Another alternative is to use the provider's native code to find if the reference is lazy and wasn't triggered, and then get the foreign key value. If it has been triggered, you can just use the reference to get the ID value, since it won't cause a query to fetch anything. This is something you would have to look into EclipseLink's source code for though.
Sorry, I cant comment so I put it here ,
I think it should be like that
#Entity
public class EdgeInnovation {
#Id
public long id;
#ManyToOne
public NodeInnovation destination;
#ManyToOne
public NodeInnovation origin;
}
And the other class is :
#Entity
public class NodeInnovation {
#Id
public long id;
#OneToMany(mappedBy="origin")
public List<EdgeInnovation> replacedEdges;
}
If I'm getting the situation wrong sorry, (Could you draw your classes with the relations so I can get it straight?)
Why not use a new construction in JPA and a custom constructor in NodeInnovation? Basically, create a transient property in NodeInnovation for use when you only want the EdgeInnovation id:
#Entity
public class NodeInnovation {
#Id #GeneratedValue private Long id;
private Integer type;
#OneToOne
private EdgeInnovation replacedEdge;
#Transient
private Long replacedEdgeId;
public NodeInnovation() {}
public NodeInnovation(Long id, Integer type, Long replacedEdgeId ) {
this.id = id;
this.type = type;
this.replacedEdgeId = replacedEdgeId;
}
...
}
Use it like so:
NodeInnovation n = em.createQuery("select new NodeInnovation(n.id, n.type, n.replacedEdge.id) from NodeInnovation n where n.id = 20", NodeInnovation.class).getSingleResult();
You didn't say how you were selecting NodeInnovation, whether directly or through a join, but either way the trick is the new NodeInnovation in the JPQL or CriteriaBuilder query.
I am aware I am quite late but some people might look for an answer to the same question - in your JPA repository you could do something like this:
#Query("SELECT new java.lang.Integer(model.id) FROM #{#entityName} model WHERE model.relationModeFieldlName.id IN :relationModelIds")
List<Integer> findIdByRelationModelIdIn(#Param("relationModelIds") List<Long> relationModelIds);

JPA Entity issue with ID

I'm building one application using JPA, I want to have a parent entity called "BaseEntity" with the attribute ID and then the rest of the entities extending this entity and then having their own attributes. The field id in the parent class is protected. However when I start the server I'm getting the following error:
Caused by: org.hibernate.AnnotationException: No identifier specified for entity: com.fgonzalez.domainmodel.User
Of course if I place the id field in the class User, it is working fine but this is not what I want. The weird thing is if I use xml files for the hibernate mappings instead of JPA, it works fine, but not with JPA. Any idea where can be the problem? Attached the code:
Base Entity:
public class BaseEntity implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
#Column(name="id")
protected Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
And User entity:
#Entity
#Table(name="users")
public class User extends BaseEntity{
/**
*
*/
private static final long serialVersionUID = 1L;
/**
*
*/
#Column(name="EMAIL",nullable=false,length=50,insertable=true,updatable=true)
private String email;
#Column(name="PASSWORD",nullable=false,length=50,insertable=true,updatable=true)
private String password;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email=email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
Thank you in advance!!
You can't do that this way: BaseEntity isn't an #Entity, so #Id shouldn't even be processed.
If Hibernate does process it while using xml, that's probably a non-portable specificity.
You could implement some entity hierarchy, but I wouldn't do it in this case. You can only extend once, and this doesn't look like a real hierarchy: only one root, shared by every class?
You can find more information on entity inheritance here: http://docs.oracle.com/javaee/6/tutorial/doc/bnbqn.html
You could use compositions instead of inheritance. In this case, just annotate your User class (which wouldn't be an #Entity) with #Embeddable, and have a field annotated with #EmbeddedId on the using class.
Still, I wouldn't do that: it seems more DRY, but it has no more benefits that replacing String everywhere with something else just to not repeat yourself (which you would then do anyway).
I would just have an #Id Long id; field in every entity, freeing them from hierarchy hell. It looks more boilerplate, but will be much easier in the long term, with no obvious disadvantage.
If you are going implement inheritance in JPA, you are not suppose to do like in java. JPA got its own implementation strategies. Have a look here and choose the one that best suits your need

EntityLIstener not called via OneToOne mapping with cascade

I have an Application object and some Form objects. Each time a Form is saved, I would like to update Application.lastUpdated. Application has an EntityListener which I want to use to set Application.lastUpdated for me.
When I save Application, the EntityListener is called and it works. However when I save Form the EntityListener is not called. I can see this via debugging and via the fact that the lastUpdated field does not change. The Form has a one-way OneToOne relationship with the Application with CascadeType.ALL.
Can I make this setup work? If not, is there a better way of doing it than manually
updating Application.lastUpdated each time I save a Form?
I'm using JPA backed by Hibernate and HSQLDB. All the annotations are JPA.
Here's the Form's relationship definition
#Entity
public class CourseForm implements Serializable {
private static final long serialVersionUID = -5670525023079034136L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
long id;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "application_fk")
private Application application;
//more fields
//getters and setters
}
The Application looks like this:
#Entity
#EntityListeners({ LastUpdateListener.class })
public class Application implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
long id;
private LocalDateTime lastUpdate;
//more fields
Application() {
}
public LocalDateTime getLastUpdate() {
return lastUpdate;
}
public void setLastUpdate(LocalDateTime lastUpdate) {
this.lastUpdate = lastUpdate;
}
//getters and setters
}
Here's the listener
public class LastUpdateListener {
#PreUpdate
#PrePersist
public void setLastUpdate(Application application) {
application.setLastUpdate(LocalDateTime.now());
}
I suspect that the above setup doesn't work because the Application object is not actually changed, so that's why #prePersist and #PreUpdate don't get invoked.
I solved the problem by having each Form implement an interface MasterForm:
public interface MasterForm {
Application getApplication();
}
Then I moved the EntityListener onto each Form and had it update the Application directly as follows:
public class LastUpdateListener {
#PreUpdate
#PrePersist
public void setLastUpdate(MasterForm form) {
form.getApplication().setLastUpdate(LocalDateTime.now());
}
This is working as expected.

Categories