Spring Data Rest: POSTing entity with relation to non-exported entity - java

I have an entity type that is exported via Spring Data Rest and has a relation to an entity which does not have it's own REST endpoint:
#Data
#NoArgsConstructor
#EqualsAndHashCode
#Entity
public class Request {
...
#NotNull
#OneToOne(cascade = CascadeType.ALL)
#Immutable
private Address address;
The "Address" type looks roughly like this plus the usual stuff for addresses ;)
#NoArgsConstructor
#Entity
#Getter
#Setter
public class Address {
#Id
#GeneratedValue
private Long id;
There is a RequestRepo that extends CrudRepository but NO repository for the address type.
This is what I do:
1. GET a particular Request entity
2. Make a change to a property of Request (not address)
3. PUT the entire Request
I noticed, in the handler's #BeforeSave annotated method, that the address property has an id of "null". So it appears that instead of looking for that Address in the database, it simply created a new one.
Well, I understand that since the id is not POSTed there's not way to know which Address in the database this is supposed to be. That makes sense.
In the handler, I want to check if the address has been changed and then do things based on that information. Therefore, I fetch the old version of the Request from the database and compare the IDs of the two Addresses which of course results in a NullPointerException, since the ID of the posted Address is null. My quick solution would be to implement equals() omitting the ID and use that method to make the comparison.
But I wonder what other ways there might be. Any ideas?

I would make an address an Embeddable rather than an Entity: an address cannot exist without a Request and is not an Entity in its own right. You are unlikely to want to query for an Address. This may differ if you had many entities sharing the same address but it's a one-one-one so you don't.
You can keep the same database structure by using #SecondaryTable.
Request:
#Data
#NoArgsConstructor
#EqualsAndHashCode
#Entity
#Table(name = "requests")
#SecondaryTable("name = "addresses")
public class Request {
#NotNull
#Embedded
private Address address;
}
Address:
#NoArgsConstructor
#Embeddable
#Getter
#Setter
public class Address {
#Column(name="town", table="addresses")
private String town;
}
Now on PUT the existing address will be updated.

Related

can't figure out how to audit for null value in not owned entity using hibernate envers

What my project have:
rsqlParser in order to parse complicated queries
Hibernate envers for audit purposes
Pretty stupid middle developer who don't know how to implement isNull rsql query
I have two Object with strict one-to-one relationship: object A which contains object B, and object B, which contains object A.
In RDS it's looks like object B has an object_a_id field
Object_A entity class
#Entity
#Getter
#Setter
#Audited
#NoArgsConstructor
public class Object_A {
#OneToOne(mappedBy = "object_a")
private Object_B object_b;
}
Object_B entity
#Entity
#Getter
#Setter
#Audited
#NoArgsConstructor
public class Object_B {
#OneToOne
#JoinColumn(
name = "object_a_id",
referencedColumnName = "id",
foreignKey = #ForeignKey(name = "object_b_object_a_fk")
)
private Object_A object_a;
Clearly you see that Object_B OWNS Object_A and when I try to perfom something simple like
return auditProperty.isNull();
I get
This type of relation (object_b) isn't supported and can't be used in queries
I guess I need somehow to make custom query where I add some object_b subselect beforehand but can't figure out how to write it.
You should probably create an issue in the issue tracker(https://hibernate.atlassian.net) with a test case(https://github.com/hibernate/hibernate-test-case-templates/blob/master/orm/hibernate-orm-5/src/test/java/org/hibernate/bugs/JPAUnitTestCase.java) that shows this limitation and ask for an improvement for this.
Usually, in ORM this is handled on the SQL level by introducing an exists subquery like:
where not exists (select 1 from object_b b where b.object_a_id = rootAlias.id)
Not sure how that works exactly in your case, but you could try to do something similar in your query.

How to prevent Spring JPA Entities from becoming optional when saving to CRUDRepository?

I was trying to learn Spring Framework and ran into a problem with saving entities into CRUD Repository. I had few Entities with automatic numeric ID generation and they work just fine, but then I tried to make a class with String being a primary key just like this:
#Entity
#Table(name = "USERS")
#Builder
public class User {
#Id
#Column(name = "USER_NAME", nullable = false)
#Getter #Setter
private String name;
#Column(name = "USER_PASS", nullable = false)
#Getter #Setter
private String pass;
}
First I was getting exceptions about this class not having a default constructor:
org.springframework.orm.jpa.JpaSystemException: No default constructor for entity: : com.company.Model.User; nested exception is org.hibernate.InstantiationException: No default constructor for entity: : com.company.Model.User
Already weird, but still I decided to change #Builder annotation into 2 constructors, one with both arguments and second with none. I tried to save the entity instance into CRUD Repository userDAO (which is nothing more than interface extending CRUDRepository) by the typical test:
User admin = new User("admin", "6aDcZ72k");
...
#Test
public void saveUserAndFindById() {
admin = userDAO.save(admin);
assertThat(userDAO.findById(admin.getName())).isEqualTo(admin);
}
The result was assertion failed because the saved entity had "Optional" type:
org.junit.ComparisonFailure:
Expected :com.company.Model.User#2c06b113
Actual :Optional[com.company.Model.User#2c06b113]
I know I'm doing something really wrong but can't figure this out. Or maybe there is a way to just prevent making it optional? There must be few other entities with the reference on this class, but these references obviously don't work because of the above issue.
First of all,jpa require the entity has a No Arguments Constructor cause it will create a instance first and then populate it.The easiest way is to add #NoArgumentsConstructor that offered by lombok on the entity class.
And then,Optional is used by spring data jpa in order to avoid NullPointException and in fact it be is useful actually.If you want to use the interface that Spring-data-jpa offered,then you have to use Optional too.You cloud look here for more info about Optional:link1,link2
By the way,I usually use it like:
ExampleEntity example=exampleRepository.findById(id).orElseThrow(()->new ExampleNotFoundException());
In this way,you dont need to deal with Optional or think about NullPointException.
or:
ExampleEntity example=exampleRepository.findById(id).orElse(null);
In this way if you cant find the target entity,then it will be null.So dont forget to check if the entity is null.
Hope it could help~~~
It is not your save(...) that is returning Optional but userDAO.findById(admin.getName(). According to the documentation, CrudReposiotry provides a findById() whose return type is Optional<T>.
public interface CrudRepository<T, ID extends Serializable>
extends Repository<T, ID> {
Optional<T> findById(ID primaryKey);
}
If you do not want Optional as return type, You will need to provide your own method to do that. For example:
public interface PeronRepository extends CrudRepository<Person, String> {
Person findById(String personId);
}

Non-entity class extending an entity class

I have a party class that's mapped to a table and is annotated with #entity like so:
#entity
#Table(name = "PARTY")
public class party{
#Id
protected long partyId;
}
I also have a class representing an address:
#entity
#Table(name = "ADDRESS")
public class address{
#Id
protected long partyId;
}
The party Id can be used as a foreign key to get an address from the address table.
Sometimes I want to return a party by itself and sometimes I want to return a party with an address. I thought I could just make a separate class that extends party but adds in the address like so:
public class partyWithAddress extends party{
#OneToMany
private List<Address> addresses;
}
But I get an error:
Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Not a managed type: partyWithAddress
I read about entity inheritance but I still am pretty lost. Can anyone help?
If your class is not entity then it wont be recognized by JPA, you need to annotate it with #Entity first , then comes which inheritance policy you would like to use , by default the singltable inheritance is used which means all children and their parent is in one table and this is recommended in case you do not have much difference between your entities, for further information about inheritance check this link
I'd highly suggest avoiding inheritance hereand just add
#OneToMany
List<Address> addresses;
to the Party entity, and handling in your code the case for that list of Addresses being empty/null.

Is it possible to force Hibernate to embed an Entity?

In my use-case, I would like to #Embedded a class C in an entity.
Another entity refers to C with #OneToMany association and therefore C is annotated with #Entity.
I am aware that this seems like bad design, yet I believe that it makes perfect sense in my case.
Is it possible to force Hibernate to embed an Entity? If I try it, Hibernate complains about a missing setter for the id property of C.
I think the problem comes from this:
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private Long id;
Why not just create the entity that you want, and in that entity, embed C as well. That way you have C in both classes, one as embedded and another as embedded of the new entity.
#Embeddable
public class Contact {
private String firstname;
private String lastname;
// getters and setters removed.
}
and here is your embedding class:
#Entity
public class Student {
#Embedded
private Contact contact;
}
and here is the new entity that embeds contact also
#Entity
public class FirmContact {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int contactId;
#Embedded
private Contact contact;
}
And finally the class that insists the contact must be an entity:
#Entity
public class Business {
#OneToOne(cascade=CascadeType.ALL)
private FirmContact contacts;
}
It'll just be a couple of extra steps in java to populate the object, but it should do the mapping you want. I hope this helps.
Hibernate doesn't allow you to treat an Embeddable as an Entity or to embed an Entity. According to Hibernate types:
an Embeddable, doesn't have an identifier, since it's state is part of an owning Entity.
an Entity cannot be embedded, because each Entity has a distinct life-cycle.
Since another class already has a #OneToMany association to class C, it's obvious you cannot turn it into an Embeddable.
More, a bidirectional #OneToMany association will perform better than an embeddable collection.
What you can do, is to use it as a #OneToOne association in the entity where you wanted to embed the C entity. You can make that target entity be the owning side of the association so that the C association is bound to the target entity life-cycle.

Persisting JPA entities with entity fields possible?

If I have an entity such as the following:
#Entity
public class Customer {
private Address address;
}
And the Address is also an entity:
#Entity
public class Address {...}
Does persisting the Customer in turn persist its contained Address? Or is this not possible at all? The idea was basically to have a main entity that consists of its fields, some of which are entities themselves that will be stored in individual tables. Some of the fields of Customer are unique in that I also would like a Customer table for that data. Unless I'm just missing it, I haven't been able to find this answer. This was something I was just curious about and I'm not currently on a machine where I can try it, so I wanted to ask first.
Thanks in advance.
This is possible and JPA basics. But you have to define the associations between entities in your entity classes.
I recommend reading a good tutorial on this topic, e.g. the Java EE6 tutorial.
You have 2 options depending on your domain model:
removing the #Entity from address and annotate it with #Embeddable
mapping the Address in the Person with: #OneToOne(cascade = {CascadeType.PERSIST})

Categories

Resources