Non-entity class extending an entity class - java

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.

Related

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

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.

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);
}

How to solve 'no primary key specified' for an Entity that inherits from another Entity (JPA)?

I want to have a superclass that is common to all document types:
#Entity
public abstract class Doc implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
protected long docId;
public long getDocId()
{
return docId;
}
public void setDocId(long docId)
{
this.docId = docId;
}
}
And I want to have child classes for each doc type:
#Entity
#Table(name = "DocTypeA")
public class DocTypeA extends Doc implements Serializable
{
// other child fields
}
But it gives an error and says that DocTypeA needs a primary key. How can I isolate the primary key and put it in the super class? Because all the subclasses will have that same id field.
I am using EclipseLink.
And my other question is: Why do I need to put #Entity in the abstract class? Being an abstract class it cannot be instantiated, so what is the point of marking it as an Entity? Is it really necessary? I will not persist the superclass. I need it only for isolating the code common in all subclasses.
The stack trace is a long one, relevant part is pasted below:
Exception Description: Entity class [class repository.DocTypeA] has no primary key specified. It should define either an #Id, #EmbeddedId or an #IdClass. If you have defined PK using any of these annotations then make sure that you do not have mixed access-type (both fields and properties annotated) in your entity class hierarchy.
According to the official JavaDoc the annotation #MappedSuperclass:
Designates a class whose mapping information is applied to the entities that inherit from it. A mapped superclass has no separate table defined for it.
which is what you are looking for. Thus abstract classes can easily be used for common attributes of entities, which in most cases a primary key or DB-generated object identifier is. Annotated fields of that abstract class will then only be mapped for concrete subclasses:
A class designated with the MappedSuperclass annotation can be mapped in the same way as an entity except that the mappings will apply only to its subclasses since no table exists for the mapped superclass itself. When applied to the subclasses the inherited mappings will apply in the context of the subclass tables.
Exchange the #Entity annotation in the abstract class Doc like so:
#MappedSuperclass
public abstract class Doc implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
protected long docId;
//...
}
and you should be good to go.
Hope it helps.

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.

How to add one-to-many association in Hibernate to an embedded type?

I have an Entity that holds the last instance of a Component:
#Entity
public class Customer {
...
#Embedded
public ServiceCall getLastServiceCall() {...}
...
}
#Embeddable
public class ServiceCall() {
public Date getDate();
public Status getStatus();
}
The ServiceCall is embedded in order to call customer.getLastServiceCall().getDate() without requiring another table join.
I want to make that association one-to-many, and start saving all ServiceCalls, while holding one embedded in the customer.
Hibernate's Docs (v3.6, which I'm using) states:
You can also use association annotations in an embeddable object (ie #OneToOne, #ManyToOne, #OneToMany or #ManyToMany). To override the association columns you can use #AssociationOverride.
and it seem that all I should do is add #OneToMany to the LastServiceCall association.
Will that work for me? If not, what are my alternatives? If yes, how will that affect 2nd level cache, and is there a limitation on updating that embedded instance (I can live with an immutable objects)?
#Embeded types are not supposed to have their own identity in the database, so I don't think you can add #OneToMany to the Customer class on the ServiceCall.
#OneToMany
#Embedded
public ServiceCall getLastServiceCall() {...}
However you can add an association to the #Embeded Service call element like so.
#Entity
pubic class HistoricalServiceCall extends ServiceCall
{
#Id
private String id;
}
#Embeddable
public class ServiceCall {
#OneToMany(fetch=FetchType.LAZY)
#JoinColumn(name="join_column_defined_on_customer_table")
List<HistoricalServiceCall> getServiceCallHistory();
}
Update: putting FetchType.LAZY on the getServiceCallHistory() is a hint to the JPA provider to wait until you call getServiceCallHistory before it does another select to pull in that association.
So with the setup I am describing if you do customer.getLastServiceCall().getDate() it will not pull
in the ServiceCallHistory before the relationship is lazy.
What you need is the following:
A Customer entity
An embeddable ServiceCall
A HistoricalServiceCall entity.
The Customer should contain an embedded ServiceCall field (the last service call).
The HistoricalServiceCall entity should have an ID, an embedded ServiceCall field (the data of the HistoricalServiceCall), and, potentially, a ManyToOne association to Customer if you want the OneToMany to be bidirectional.
The Customer should have a OneToMany association to HistoricalServiceCall.

Categories

Resources