I started developing in Java quite recently, and my client is also a developer who is developing in Java since it was released.
So when he says "we have a good reason why don't we use transient fields in our project", I didn't ask what those reasons are. But, back to the question:
I have two classes:
POJO, which is used solely to generate JSON:
public class BaseSector implements Serializable {
private String id;
private String name;
private String parentId;
Entity:
public class Sector {
#Column(length = 36)
private String id;
#Column(length = 40)
private String name;
#Column(length = 36)
private String parentId;
// ... Bunch of other fields
Is there any way for an Entity class to extend this POJO, and add Column annotations dynamically? Or have POJO as an interface? Or use entity class in POJO constructor?
Earlier we had something like this:
for (Sector sector : sectors) {
BaseSector baseSector = new BaseSector();
baseSector.setId(sector.getId());
baseSector.setName(sector.getName());
baseSector.setParentId(sector.getParentId());
}
But I changed that by using BaseSector in HQL constructor...
Btw, we also have SectorInfo and SimpleSectorInfo which also extend BaseSector, but that's a different subject..
A TRANSIENT field tells your ENTITY class that this particular field should not be persisted in the DB. #Transient annotation is used to ignore a field to not persist in database in JPA, where as transient key word used to ignore a field from serialization. The field annotated with #Transient still can be serialized, but the field declared with transient keyword not to be persisted and not to be serialized.
A POJO can be extended by an ENTITY and vice-versa. This is stated in JPA specification.You can find more examples at the below links :
Link:1 : JPA Non-Entity SuperClass
Link 2 : JPA Specification
You can achieve this by using an annotation : #javax.persistence.MappedSuperclass
It states : A superclass is treated as non-entity class if no mapping related annotations such as #Entity or #MappedSuperclass are used on the class level.
This means your superclass will be treated as a non-entity class here if you do not use the above annotations in your superclass.
How to Construct the classes :
SUPERCLASS which also a POJO for your JSON object
#MappedSuperclass
public class BaseSector implements Serializable {
private String id;
private String name;
private String parentId;
}
ENTITY class :
#Entity
#Table(name = "sector")
public class Sector extends BaseSector {
#Column(length = 36)
private String id;
#Column(length = 40)
private String name;
#Column(length = 36)
private String parentId;
// ... Bunch of other field
}
You can also override some property defined by BaseSector in your ENTITY - Sector
You need to use
#AttributeOverride // for single property
#AttributeOverrides // override more than one property
Related
I have a Java Bean class which has some attributes having some Object type as datatype. Again that Object is having attributes which is again have some Object datatype and so on...
public class MyService {
private String id;
private String href;
private Category category;
}
public class Category {
private String id;
private Type type;
}
public class Type {
private String id;
private SomeObject someObject;
}
so on ...
Now I want to create JPA Entity classes for the class MyService with all the Mappings (OneToMany etc.). I know from database table we can generate the Entity classes but can we do it from the Java bean classes?
Because if I do manually it will take much time. So please suggest if I can generate those Entity classes or some other alternative ways instead of manually creating those Entity classes.
Animal.java
#Data
#Entity
public class Animal implements MyEntityInterface {
public enum Sex {MALE, FEMALE}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
private Sex sex;
private boolean castrated;
#OneToMany
private List<Symptom> symptoms;
}
AnimalDTO.java
#Getter
#Setter
public class AnimalDTO implements Serializable {
private long id;
private String name;
private Animal.Sex sex;
private boolean castrated;
private List<Long> symptoms;
}
I wish for a list of Symptoms to be automatically mapped to a list of ID's. This could be achieved in many ways, such as creating a TypeMap, creating a Converter or even just by creating a method in AnimalDTO.java:
public void setSymptoms(List<Symptom> symptoms) {
if (symptoms != null)
this.symptoms = symptoms.stream().map(s -> s.getId()).collect(Collectors.toList());
}
But now imagine it's not only Symptoms, but 50 other fields too. That's a lot of code for the same functionality. And then, it's not only Animal to AnimalDTO, but another 30 different classes with their respective DTOs too.
Also, that still leaves the way back open. From ID to entity. This can (in theory) be achieved easily with the following pseudocode:
List<EntityMemberField.class> list;
for (var entityid : listOfEntityIDsOfDto) {
Object persistedObject = entityManager.find(EntityMemberField.class, entityid);
list.add(persistedObject);
}
...
ModelMapperDestination.setField(list);
This is the same for absolutely every Entity/DTO and should automatically happen for every Entity relationship where the Entity implements MyEntityInterface.
An idea how I could achieve that would be overriding MappingEngineImpl.java from ModelMapper which I register as a Spring Service and inject the EntityManager into, but how could I get ModelMapper to use mine? Or is there maybe an easier way?
The goal is to have a fairly automated conversion from Spring Entities to their corresponding DTO by... just calling modelMapper.map(entity, EntityDTO.class);
I've one persistent class that has one transient field representing the API version of this class (subset of the fields that I user for api requests). This field is #Transient as I simple use the other fields to create it.
The problem is that hibernate uses the default empty constructor to instantiate the class and reflection to access the fields... so i can't instantiate my transient class on constrorctor nor on the call of setter methods
I tried to anotate the getter method instead of the field to force hibernate to use the setter, but it didn't work
I tried to use #Access(AccessType.PROPERTY) on the fields but it didn't work
how to force hibernate call setter methods to fill class fields?
#Entity
public class User {
#Transient
private ApiUser tempUser = new ApiUser ();
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Access(AccessType.PROPERTY)
#Column(nullable = false)
private String name;
#Access(AccessType.PROPERTY)
#Column(nullable = false, unique = true)
private String username;
#Access(AccessType.PROPERTY)
#Column(nullable = false)
private String userId;
//lots of others fields//
public void setUsername(String username) {
this.username = username;
this.tempUser.setUsername(username);
}
public void setUserId(String userId) {
this.userId = userId;
this.tempUser.setId(Long.parseLong(userId));
}
By default the access type is defined by the place where you put your identifier annotation (#Id). If you put it on the field - it will be AccessType.FIELD, if you put it on the getter - it will be AccessType.PROPERTY.
Sometimes you might want to annotate not fields but properties (e.g. because you want to have some arbitrary logic in the getter or because you prefer it that way.) In such situation you must define a getter and annotate it as AccessType.PROPERTY.
As far as I remember, if you specify either AccessType.FIELD or AccessType.PROPERTY on any of the entity fields / methods you must specify the default behaviour for the whole class. And that's why you need to have AccessType.FIELD on the class level (despite that AccessType.FIELD is the default value.)
Now, if you wouldn't have #Transient on the phnnumber field, the JPA would provide you with a 3 columns table:
id,
phnnumber,
getphnnumber.
That's because it would use AccessType.FIELD for all of the entity fields (id and phnnumber) and, at the same time, it'd use AccessType.PROPERTY for your getter (getPhnnumber()).
You'll end with phone number mapped twice in the database.
Therefore, the #Transient annotation is required - it means that the entity won't store the value of the field in the underlying storage but the value returned by your getter.
The strategy I'm taking to implementing a maker-checker scenario is through using multiple tables. Currently, I'm using Hibernate 4.2 (annotations). The following scenario is what I would like to achieve. However, I'm having problems with the multi-level inheritance.
The basic idea is that there are two tables (pending and approved). When an add() occurs, the entry is inserted into the pending table. When that entry is approved, it is removed from the pending table and inserted into the approved table.
Policy (the policy)
|
+ -- Pending (maker information)
|
+ -- Approved (checker information)
So, class Policy is the class that defines the necessary fields for a policy. To keep this post shorter, the fields are not be shown.
#MappedSuperclass
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) // problem
public abstract class Policy { ... }
The Pending class is for the newly-added Policy that is awaiting approval and it has information on the maker/adder.
#Entity
#Table(name = "pending")
public class Pending extends Policy {
#Column(name = "adder", ...)
private String adder;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "time_added", ...)
private Date timeAdded;
}
The Approved class is for approved entities and it contains additional information on the approver in addition to the information from the Pending class.
#Entity
#Table(name = "approved")
public class Approved extends Pending {
#Column(name = "approver", ...)
private String approver;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "time_approved", ...)
private Date timeApproved;
}
My first thought was to try TABLE_PER_CLASS. However, it resulted in the following runtime error: org.hibernate.MappingException: Cannot use identity column key generation with <union-subclass> mapping for: .... The solution for this is to modify the base class #GeneratedValue(strategy = GenerationType.IDENTITY) to #GeneratedValue(strategy = GenerationType.TABLE). However, modifying that class is beyond my scope as it is shared across multiple projects.
Just for the heck of it, I tried the other two strategies. Obviously, SINGLE_TABLE resulted in one table, with an extra column DTYPE. Not what we wanted. JOINED resulted in two tables, but the approved table has a foreign key to the pending table. Since we wanted to remove an entry from the pending table and move it to the approved table, this would not work for us.
Currently, my solution is to as follows, which is basically copy and paste the code from the Pending class into the Approved class.
#Entity
#Table(name = "approved")
public class Approved extends Policy {
#Column(name = "adder", ...)
private String adder;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "time_added", ...)
private Date timeAdded;
#Column(name = "approver", ...)
private String approver;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "time_approved", ...)
private Date timeApproved;
}
This solution seems counter-intuitive as it duplicates code. Is there a solution that does not require code duplication and keeps the maker-checker process that way it currently works?
After experimenting with the suggested approach by #kostja, I arrived at the following solution.
The maker class encapsulates information pertaining to the maker, which is also an #Embeddable class.
#Embedabble
public class Maker {
#Column(name="maker_id", ...)
private String makerId;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="time_added", ...)
private Date timeAdded;
}
Similarly, the checker class also encapsulates information pertaining to the checker, which is also an #Embeddable class.
#Embedabble
public class Checker {
#Column(name="checker_id", ...)
private String makerId;
#Temporal(TemporalType.TIMESTAMP)
#Column(name="time_approved", ...)
private Date timeApproved;
}
The payload is an #Embeddable class. By making the payload an #Embeddable class, the Maker and Checker can be reused for multiple payloads.
#Embeddable
public class Payload { ... }
For example, given two different payloads that requires maker/checker. One of the payload requires 2 checker.
#Embeddable
public class PayloadA { ... }
#Embeddable
public class PayloadB { ... }
Then we define the following two tables for PayloadA.
#Entity
#Table("a_pending")
public class PendingA {
#Embedded
private PayloadA payload;
#Embedded
private Maker maker;
}
#Entity
#Table("a_approved")
public class ApprovedA {
#Embedded
private PayloadA payload;
#Embedded
private Maker maker;
#Embedded
private Checker checker;
}
Similarly, for PayloadB define two tables. And PayloadB requires two checkers.
#Entity
#Table("b_pending")
public class PendingB {
#Embedded
private PayloadB payload;
#Embedded
private Maker maker;
}
#Entity
#Table("b_approved")
public class ApprovedB {
#Embedded
private PayloadB payload;
#Embedded
private Maker maker;
#Embedded
#AttributeOverrides(value = {
#AttributeOverride(name="checkerId",column="checker1_id"),
#AttributeOverride(name="timeApproved",column="checker1_time_approved"),
})
private Checker checker1;
#Embedded
#AttributeOverrides(value = {
#AttributeOverride(name="checkerId",column="checker2_id"),
#AttributeOverride(name="timeApproved",column="checker2_time_approved"),
})
private Checker checker2;
}
I hope this solution should be general and flexible enough.
I would use a different approach for this. I assume Policy is your entity - the one carrying the real payload. You would like to add some metadata to it. Inheritance does not look as a good fit for this to me. A Pending is not an Policy and an Approved is not a Pending.
Instead of inheritance, I would model the metadata as separate, unrelated entities and create 1-1 relationships to the payload entity. Or many-to-one, if you need multiple approvals.
This way, you have a better decoupled data model and a more normalized DB structure. This gives you more flexibility. You can have single or multiple approvals, change the Approval model without changing the payload entity, and have a better focused payload entity, unburdened by metadata.
The entities could look like this:
#Entity
public class Policy{
#OneToOne
private Creation creation;
#OneToMany(mappedBy="policy")
private List<Approval> approvals;
...
}
Creation:
#Entity
public class Creation{
#OneToOne
private Policy policy;
private String creator;
#Temporal(TemporalType.TIMESTAMP)
private Date createdAt;
...
}
Approval:
#Entity
public class Approval {
#ManyToOne
private Policy policy;
private String approver;
#Temporal(TemporalType.TIMESTAMP)
private Date approvedAt;
..
}
Has:
#MappedSuperclass
class Superclass {
#Id
#Column(name = "id")
protected long id;
#Column(name="field")
private long field;
}
and
#Entity
class Subclass extends Superclass {
}
How to annotate inherited id with #GeneratedValue and field with #Index within Subclass?
How to annotate inherited id with #GeneratedValue and field with #Index within Subclass?
AFAIK, you can't. What you can do is overriding attributes and associations (i.e. change the column or join column) using the AttributeOverride and AssociationOverride annotations. But you can't do exactly what you're asking.
For the GeneratedValue, consider using XML mapping to override the strategy if you don't want to declare it in the mapped superclass.
For the Index (which is not a standard annotation by the way), did you actually try to declare it at the table level using Hibernate's Table annotation instead (I'm assuming you're using Hibernate)?
#Table(appliesTo="tableName", indexes = { #Index(name="index1", columnNames=
{"column1", "column2"} ) } )
creates the defined indexes on the
columns of table tableName.
References
JPA 1.0 Specification
Section 2.1.9.2 "Mapped Superclasses"
Section 9.1.10 "AttributeOverride Annotation"
Section 9.1.11 "AttributeOverrides Annotation"
Section 9.1.12 "AssociationOverride Annotation"
Section 9.1.13 "AssociationOverrides Annotation"
Hibernate Annotations Reference Guide
2.4. Hibernate Annotation Extensions
Chapter 3. Overriding metadata through XML
As for #GeneratedValue, it is possible to do like this:
#MappedSuperclass
class Superclass {
#Id
#Column(name = "id")
#GeneratedValue(generator = "id_generator")
protected long id;
#Column(name = "field")
private long field;
}
#Entity
#SequenceGenerator(name = "id_generator", sequenceName = "id_seq")
class Subclass extends Superclass {
}
You might be able to do this if you apply the annotations to the accessor methods instead. (I haven't tried this, so I can't guarantee that it'll work.)
#MappedSuperclass
public class Superclass {
#Id
#Column(name = "id")
public long getId() {
return id;
}
.
#Entity
public class Subclass extends Superclass {
#GeneratedValue
public long getId() {
return super.getId();
}
Just in case anyone else searches for this, I used the following code which adds in some overhead, but for processing Field annotations only shouldn't add that much:
private List<Field> getAllFields() {
List<Field> fieldList = new ArrayList<Field>();
// Add all fields from the current class
fieldList.addAll(Arrays.asList(mElement.getClass().getDeclaredFields()));
// Use an index to iterate over mElement's parent types
Class clazz = mElement.getClass();
// Get any fields from the parent class(es)
while (clazz.getSuperclass() != null) {
fieldList.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));
// Set it to that parent class
clazz = clazz.getSuperclass();
}
return fieldList;
}
The returned list would contain all fields for all parent and child classes with mElement being the object you are searching for annotations from. Hope this helps.