how to force hibernate call setter methods to fill class fields? - java

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.

Related

Can we use Composite Primary Key Mapping in spring data elastic search

I have an entity 'Product' and I want the primary key in ES to be used as a combination of 'id' and 'name' attributes. How can we do that using spring data elastic search.
public class Product {
#Id
private String id;
#Id
private String name;
#Field(type = FieldType.Keyword)
private Category category;
#Field(type = FieldType.Long)
private double price;
#Field(type = FieldType.Object)
private List<ValidAge> age;
public enum Category {
CLOTHES,
ELECTRONICS,
GAMES;
}
}
One way to achieve this would be the following:
first rename your id property, I changed it to documentId here. This is necessary, because in Spring Data
Elasticsearch an id-property can be either annotated with #Id or it can be namend id. As there can only be one
id-property we need to get this out of the way. It can have the name id in Elasticsearch, set by the #Field
annotation, but the Java property must be changed.
second, add a method annotated with #Id and #AccessType(AccessType.Type.PROPERTY) which returns the value you
want to use in Elasticsearch.
third, you need to provide noop-setter for this property. This is necessary because Spring Data Elasticsearchsoe
not check the id property to be read only when populating an entity after save or when reading from the index.
This is a bug in Spring Data Elasticsearch, I'll create an issue for that
So that comes up with an entity like this:
#Document(indexName = "composite-entity")
public class CompositeEntity {
#Field(name="id", type = FieldType.Keyword)
private String documentId;
#Field(type = FieldType.Keyword)
private String name;
#Field(type = FieldType.Text)
private String text;
#Id
#AccessType(AccessType.Type.PROPERTY)
public String getElasticsearchId() {
return documentId + '-' + name;
}
public void setElasticsearchId(String ignored) {
}
// other getter and setter
}
The repository definition would be straight forward:
public interface CompositeRepository extends ElasticsearchRepository<CompositeEntity,
String> {
}
Remember that for every method that needs an Elasticsearch Id, you'll need to create like it's done in the entity
class.
I am not sure about spring data elasticsearch but spring jpa provides the facility of defining composite primary key by using #IdClass where we can define a separate class(let us say class A) in which we can define all the fields which we want to be a part of composite key Then we can use #IdClass(A.class) in entity class and use #Id annotation on all the fields which should be the part of the composite key
you can refer to this article, although I am not sure whether the same concept will be applicable for spring data es - https://www.baeldung.com/jpa-composite-primary-keys

Which one is the best practice for choosing data type of a property in entity class Wrapper class or Primitive type in JPA/Hibernate?

There are two options to choose the data type for the property in entity class either take it as primitive data type or keep it as wrapper class.
I just want to know which one is consider as the best practice and why ?
Option-I
#Entity
class User {
#Id
private int uuid;
private long code;
private boolean isActive;
// setter & getter
}
Option-II
#Entity
class User {
#Id
private Integer uuid;
private Long code;
private Boolean isActive;
// setter & getter
}
It does not make any difference for Hibernate, as it uses the same Hibernate type to represent them.
But with primitive type you can't distinguish the default value of a primitive int 0 from an assigned 0 while there is no possible ambiguity with a null (a null id always means a new entity), which is why one should prefer to use a nullable wrapper type.

Using POJO as a base for hibernate entity

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

JPA: Ignore field on Fetch, but save all while saving it. Is there any annotation attribute to do so?

I have the following Entity. In this I want to fetch all data except phoneNumber. What will be the best solution? It would be fine if I could do it with annotation.
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "employee_name")
private String name;
#Column(name = "gender")
private char gender;
#Column(name = "date_of_birth")
private String dob;
#Column(name = "skills")
private String[] skills;
#Column(name = "phone_number")
private String phoneNumber;
//getter setter
}
To tell what will be the best way to do this you have to say why you want to do this and what you want to achieve.
There are many options:
omit the getter
use a projection (DTO or interface)
use inhreitance
use inheritance with #MappedSuperclass
Can you please use #Transient on your field. If it is a subclass of anyclass then on class level please use #Embedded.
If not then you need to read this as this is always lazy fetch From Hibernate, Chapter 19. Improving performance:
Lazy attribute fetching: an attribute or single valued association is fetched when the instance variable is accessed. This approach requires buildtime bytecode instrumentation and is rarely necessary.
You can use #Column(insertable = true, updatable = false) and I am not sure if we can ignore while fetching using entity. you can achieve your requirements using JPA Projections
Also,#JsonIgnore may be useful. it is used to tell Jackson to ignore a certain property of a Java object but

Why JPA Transient annotation have method in Target?

Can anyone explain using an example as to why the #Transient annotation in JPA has #Target method as well?
I am referring to documentation http://docs.oracle.com/javaee/5/api/javax/persistence/Transient.html
#Target(value={METHOD,FIELD})
Thanks in advance!
In JPA entity you can annotate fields or methods (getters). The #Id annotation dictates this, meaning if you put #Id on a field then all your annotations should go on fields but if you put it on, for example, #Id Long getId() then other annotations should follow. That's why #Transient can be on a method as well.
For example, if you have this
#Id
private Long id;
#Transient
private String someTransientField;
private Long getId() {
return this.id;
}
private String getSomeTransientField() {
return this.someTransientField;
}
then someTransientField would be treated as transient. But if #Id would stay on the field, and you move #Transient to private String getSomeTransientField() then someTransientField would be treated as persistent, since #Id is on the field and therefore all other annotations are expected to be on fields as well.
So the case where #Transient would work on the method is this
private Long id;
private String someTransientField;
#Id
private Long getId() {
return this.id;
}
#Transient
private String getSomeTransientField() {
return this.someTransientField;
}
#Target annotation lets you define where this annotation can be used, e.g., the class, fields, methods, etc. indicates which program element(s) can be annotated using instances of the annotated annotation type.
#Target(value={METHOD,FIELD}) means that the annotation can only be used on top of types (methods and fields typically).you can leave the target out all together so the annotation can be used for both classes, methods and fields.
In JPA #Target – Marks another annotation #Transient to restrict what kind of java elements the annotation may be applied to.
It means the annotation can be used on Field or method.
If the field is annotated, the field will be accessed using reflection.
If method(getter) is annotated, then the getter method will be used to access it.

Categories