Why JPA Transient annotation have method in Target? - java

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.

Related

Which hibernate entity inheritance strategies is required

My base entity that is common part for many other entity:-
#MappedSuperclass
public abstract class IdBase {
#GeneratedValue
#Column(name = "id")
private Long id;
#Version
private Long version;
#CreatedBy
#Column(name = "created_by")
private String createdBy;
#CreatedDate
private Instant created;
#LastModifiedBy
#Setter(AccessLevel.PRIVATE)
#Column(name = "updated_by")
private String updatedBy;
#LastModifiedDate
#Setter(AccessLevel.PRIVATE)
private Instant updated;
}
One of the entity as follows:-
#Entity
#Table(name="TBL_SUB_EMPLOYEES")
public class SubEmployeeEntity extends IdBase {
#Column(name="sub_title")
private String subTitle;
#Column(name="sub_role")
private String subRole;
}
My generic repository as:-
#Repository
public interface AuditRepository<E extends IdBase> extends JpaRepository<E, Long>, JpaSpecificationExecutor<E> {
}
When I try to query SubEmployeeEntity by the generic repository I got error:-
Unable to locate Attribute with the the given name [subTitle] on this
ManagedType [com.test.IdBase]; nested exception is
java.lang.IllegalArgumentException: Unable to locate Attribute with
the the given name [subTitle] on this ManagedType [com.test.IdBase]
IdBase is common class for many entity and I kept only the common column here. I only showed SubEmployeeEntity. I have same kind of entity inherited form IdBase as well. Why is it looking for subTitle in IdBase. How do I fulfill my requirements?
Seems like you are using JPA specifications and within the specification try to access subtype properties although the repository you are using has the type bound IdBase. To make this work, you would have to subclass the repository for every concrete type and also inject that subtype of repository which I guess you don't want to do. If that's the case, you should use TREAT in your specifications to access the subtype properties.
You have to imagine that the type variabbe has no effect as Spring can never observe what you use on your use-site. Only if create a subtype and inject that subtype, Spring can resolve the type variable to the concrete subtype. This leads to the fact that Spring creates a JPA root for the type bound IdBase so now you have to treat the alias to be able to access properties through JPA Criteria.

Spring Data Rest nested property sort with JsonUnwrapped

So, I have this class, using #EmbeddedId and #JsonUnwrapped annotations
#Entity
class Order {
#EmbeddedId #JsonUnwrapped
private OrderPK pk;
private String field1;
private String field2;
//...getters, setters
}
And this is the primary key class
#Embeddable
class OrderPK implements Serializable {
private String orderNumber;
private String company;
//...getters, setters
}
I wanted to make Spring Data REST sorting treat the fields in the PK class as top-level instead of nested. Basically, I want to use this one: .../orders?sort=orderNumber,desc, instead of .../orders?sort=pk.orderNumber,desc. I couldn't find any way to do this in the documentations.
You can no longer achieve your goal, if you are using the #EmbeddedId option to implement your composite key. The reason is that the attributes under the hood of EmbeddedId class cannot be taken out as flat parameters.
Note that #JsonUnwrapped is not intended to perform that task.
You can use the following option instead of #EmbeddedId to achieve your goal.
Use #IdClass. Follow this example.

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

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.

Will adding an annotation break Java Serialization?

I am in the process of rewriting a very old java app to Spring Boot and Hibernate 5. Part of this task requires that I replace our XML configuration (both Spring and Hibernate) with annotations.
I have the following question. Let's assume that the application contains a class as such:
public class MyObject implements Serializable {
private static final long serialVersionUID = 81848571841847187L;
private String id;
private String name;
//getters and setters...
}
This class Serialized across a network, and is included in a "common" jar, which classers must include, in order to deserialize on their end.
Let's assume that I add a few Hibernate and JPA annotations to the class
#Table(...)
#Entity
public class MyObject implements Serializable {
private static final long serialVersionUID = 81848571841847187L;
#Id
#Column(...)
private String id;
#Column(...)
private String name;
//getters and setters...
}
My question is: if the caller (who deserializes the above Object) does not have those annotations in his classpath, will serialization fail?
Only Annotations with RETENTION=RUNTIME used in byte code, but Serialization works with object fields, not with classes.
but its important to understand that Annotations can be used by custom serializer.
for example this is how #Transient exclusion is implemented.
so the next thing is to check what type of Serialization mechanism is used.
elad

Applying annotations to fields inherited from #MappedSuperclass

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.

Categories