Spring Data Rest nested property sort with JsonUnwrapped - java

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.

Related

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

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

Jackson use getter for a specific property

I have a JPA transient property in an entity which has a calculated value based on multiple fields in the POJO. All these calculations are done in the GETTER of that property.
But, Jackson doesnt seem to be using the GETTER when creating the JSON for that POJO.
How do I configure Jackson to use getter for the property?
My POJO looks something like below
#Entity
public class ProductSummaryEntity implements Serializable {
#Basic
private String field1;
// GETTER and SETTER for Field1
#Basic
private String field2;
// GETTER and SETTER for Field2
#Transient
private String field3;
public String getField3(){
setField3(field1 + field2);
return this.field3;
}
public void setField3(String temp){
this.field3=temp;
}
}
This link to a blog by #sghill has been posted on SO before and shows you how to customize the serialization process: https://www.sghill.net/how-do-i-write-a-jackson-json-serializer-deserializer.html
Essentially, annotate your POJO with #JsonSerialize(using = CustomSerializer.class) and then implement a class CustomSerializer that's extending from JsonSerializer. In your implementation you can build the JSON however you like and calculate values on the fly or call your getters.
No, I don't think you can serialize transient field unless there is something latest is there.

JPA OneToMany and ManyToOne with a composite key is generating a third table

I'm mapping a Filter ---< FilterColumn where Filter presents cardinality one and FilterColumn N. So the mapped classes are:
#Entity
public class Filter implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private String name;
private String caption;
#OneToMany(cascade = CascadeType.MERGE, targetEntity = FilterColumn.class)
private Set<FilterColumn> columns;
// setters and getters
}
#Entity
public class FilterColumn implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
private FilterColumnId id;
private String caption;
// getters and setters
#Embeddable
public static class FilterColumnId implements Serializable {
private static final long serialVersionUID = 1L;
#ManyToOne
private Filter filter;
#Column
private String name;
// getters and setters
}
}
But when I start the application with drop-create instruction the following 3 tables are created:
Filter PK(name)
FilterColumn PK(filter_name, name)
Filter_FilterColumn PK(filter_filter_name, filterColumn_filter_name, filterColumn_name)
What I really want is just two tables like:
Filter PK(name)
Filter_Column PK(name, filter_name)
Why do I receive this result? Is there something wrong with my mapping? What should I change?
Thanks in advance.
I think you need a mappedBy on the #OneToMany. Without that, the mapper doesn't know that it can look at the filtercolumn table to find the entities associated with a Filter, so it generates the filter_filtercolumn table.
Not sure off the top of my head how you to a mappedBy with a composite key. Given that you're using an #EmbeddedId, i think it's simply mappedBy = "id".
Can you use a #ManyToOne in a key class like that? Is that a Hibernate extension over and above the JPA spec? Wouldn't you normally need a #MapsId in there somewhere?
Try adding a #JoinColumn annotation on the Filter member of your composite id. The actual column would be whatever the id of the of the Filter table is (or just leave it without a name if you let hibernate generate it all).
Let me know if this works as I had a similar problem and solved it using the above so I do know it's possible. The only other thing mine has is a #ForeignKey annotation but I think hibernate will take care of that for you -- I just did mine because I wanted to stick to a naming convention.

Storing Objects in columns using Hibernate JPA

Is it possible to store something like the following using only one table? Right now, what hibernate will do is create two tables, one for Families and one for people. I would like for the familymembers object to be serialized into the column in the database.
#Entity(name = "family")
class Family{
private final List<Person> familyMembers;
}
class Person{
String firstName, lastName;
int age;
}
This is an horrible design and I'm really not recommending it (you should just create another table) but it is possible.
First, you'll need to use a byte[] attribute to hold a serialized version of the list of persons that will be stored in a BLOB in the database. So annotate it's getter with #Lob (I would make the getter and setter private to not expose them). Then, expose "fake" getter and setter to return or set a List<Person> from the byte[]. I'm using SerializationUtils from Commons Lang in the sample below (provide you own helper class if you don't want to import this library) to serialize/deserialize on the fly to/from the byte[]. Don't forget to mark the "fake" getter with #Transcient or Hibernate will try to create a field (and fail because it won't be able to determine the type for a List).
#Entity(name = "family")
class Family implements Serializable {
// ...
private byte[] familyMembersAsByteArray;
public Family() {}
#Lob
#Column(name = "members", length = Integer.MAX_VALUE - 1)
private byte[] getFamilyMembersAsByteArray() { // not exposed
return familyMembersAsByteArray;
}
private void setFamilyMembersAsByteArray((byte[] familyMembersAsByteArray() { // not exposed
this.familyMembersAsByteArray = familyMembersAsByteArray;
}
#Transient
public List<Person> getFamilyMembers() {
return (List<Person>) SerializationUtils.deserialize(familyMembersAsByteArray);
}
public void setParticipants(List familyMembers) {
this.familyMembersAsByteArray = SerializationUtils.serialize((Serializable) familyMembers);
}
}
Don't forget to make the Person class Serializable and to add a real serialVersionUID (I'm just showing a default here):
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
// ...
private String firstName, lastName;
private int age;
}
But, let me insist, this is an horrible design and it will be very fragile (changing Person might require to "migrate" the content of the BLOB to avoid deserialization issues and this will become painful. You should really reconsider this idea and use another table for the Person instead (or I don't get why you use a database).
#Type(type = "serializable")
private List<Person> familyMembers;
if you can't use hibernate annotations try this:
#Lob
private Serializable familyMembers;
public List<Person> getFamilyMembers(){
return (List) familyMembers;
}
public void setFamilyMembers(List<Person> family){
familyMembers = family;
}
Annotate the property with #Column and define the type to be ArrayList, not just List. And make Person implement Serializable.
But you should do this only if your motives are very clear, because this is the correct solution in some very rare cases. As Pascal noted, if you ever have to change Person you'll have headaches.
You can create pseudoproperty (getter and setter) which accepts/returns the serialized form, and annotate the familyMembers with #Transient. This would also need to annotate the getters, not fields, for all other properties.

Categories