This happened to me other times, but this time the reason is different.
I reduced the scenario where it can be reproduced down to two entities, Child and Parent:
Child:
#Table(name = "Childs")
#Entity
#IdClass(KeyChild.class)
#Builder
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
public class Child {
#Id
#ManyToOne
private Parent parent;
#Id
private int id0;
}
Parent:
#Table(name = "Parents")
#IdClass(KeyParent.class)
#Entity
#NoArgsConstructor
#AllArgsConstructor
#Builder
#Getter
#Setter
public class Parent {
#Id
private String id1;
#Id
private int id2;
}
The composite keys:
public class KeyChild implements Serializable {
private KeyParent parent;
private int id0;
}
and
public class KeyParent implements Serializable {
private String id1;
private int id2;
}
The problem happens when trying to save a Child (specifying the parent as you would spect).
An example controller:
#Controller
public class ParticiparController {
#Autowired
ParentRepository parentRepository;
#Autowired
ChildRepository childRepository;
#GetMapping("/test/")
public String evento() {
Parent p = Parent.builder().id("test").id2(1).build();
parentRepository.save(p);
childRepository.save(Child.builder()
.parent(p)
.id(0)
.build());
return "test";
}
}
The save method of the child repository throws:
org.springframework.beans.NotWritablePropertyException: Invalid property 'id1' of bean class [com.example.myProject.Entities.Keys.KeyParent]: Bean property 'id1' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
I also tried without Lombok (just generating the getters and setters), but I got same result. I'm pretty sure setters are not the problem.
Update: There were, but not the entity ones... It seems that are only required in some cases.
As it suggested in the error message you should have getters/setters in the KeyParent which correspond to the appropriate getters/setters of the Parent entity fields annotated by the #Id.
Related
I have an #Entity class, with an #Id annotation and a #OneToOne annotation on the same field. Usually this would not be a problem, but the entity class of the field with these annotations uses a composite key. This is causing more complications than I anticipated.
Here is the entity class that is posing the problem:
#Entity
public class ReportDetails implements Serializable {
#Id
#OneToOne
private MachineLine machineLine;
}
And here is the MachineLine entity class that is being used as an ID in ReportDetails:
#Entity
#IdClass(MachineLine.MachineLineKey.class)
public class MachineLine {
#Id
#ManyToOne
private Machine machine;
#Id
private long lineIndex;
public static class MachineLineKey implements Serializable {
private Machine machine;
private long lineIndex;
}
}
I have left out any extra fields and the getters and setters from these class definitions, to save space.
When I try to run my application it gives the following exception:
java.lang.IllegalArgumentException: This class [class ReportDetails] does not define an IdClass
When I put an #IdClass annotation on ReportDetails it then requires defining the individual fields of whatever class I define in #IdClass, like in MachineLine. However, I am trying to avoid doing this, in favour of having the whole MachineLine entity returned whenever a ReportDetails entity is retrieved from the database.
Is there a way of having MachineLine as the ID field of ReportDetails, without having to define extra fields within ReportDetails?
This is what JPA calls a "derived identity". You might try something like this:
ReportDetails:
#Entity
public class ReportDetails implements Serializable {
// all attributes map by the relationship: AttributeOverride is not allowed
#EmbeddedId
private MachineLine.Id id;
#MapsId
#JoinColumns({
#JoinColumn(name="machineId", referencedColumnName="machineId"),
#JoinColumn(name="machineLineIndex", referencedColumnName="index")
})
#OneToOne
private MachineLine machineLine;
// ...
}
MachineLine:
#Entity
public class MachineLine {
#EmbeddedId
private Id id;
#MapsId("machineId") // maps machineId attribute of embedded id
#ManyToOne
private Machine machine;
// ...
#Embeddable
public static class Id implements Serializable {
private long machineId; // corresponds to PK type of Machine
private long index;
// ...
}
}
Machine:
#Entity
public class Machine {
#Id
private long id;
#OneToMany(mappedBy = "machine")
private List<MachineLine> lines;
// ...
}
Derived identities are discussed (with examples) in the JPA 2.2 spec in section 2.4.1.
How can I tell hibernate to ignore a field during schema auto generation?
In this special case: the field is inherited from a parent abstract class, so I cannot just comment it out!
I tried using #Transient, but the field is still autogenerated in the schema.
#MappedSuperclass
public abstract class BaseEntity {
private String someField;
//getter+setter
}
#Entity
public class MyEntity extends BaseEntity {
#Transient //I want to ignore this field during hibernate.ddl.auto
#Override
public String getSomeField() {}
}
Add the Transient annotation in the super class:
#MappedSuperclass
public abstract class BaseEntity {
#Transient
private String someField;
}
#Entity
public class MyEntity extends BaseEntity {
}
I'm trying to create a jpa repository but there is a problem with a foreign-key primary-key. Although it is specified in the abstract base class (MessageDestination), it seems to be invisible from the repository of specialized MessageDestination class (e.g. MessageDestinationRoom).
[...] nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'messageDestinationRoomDAO': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: This class [class com.chat.message.entity.MessageDestinationRoom] does not define an IdClass
#Entity #Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Message implements Serializable {
#Id #GeneratedValue(strategy = GenerationType.TABLE)
private Long id;
#OneToOne(targetEntity = MessageDestination.class,
cascade=CascadeType.ALL, mappedBy="msg")
#NotNull
private MessageDestination dest;
//...
}
#Entity #Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class MessageDestination implements Serializable {
#Id #OneToOne(cascade=CascadeType.ALL)
private Message msg;
}
#Entity
public class MessageDestinationRoom extends MessageDestination {
#OneToOne #NotNull
private Room destRoom;
//...
}
public interface MessageDestinationRoomDAO
extends JpaRepository<MessageDestinationRoom, Message> {
public Set<MessageDestinationRoom> findMessageDestinationRoomByDestRoom
(Room dest);
}
To solve the issue I saw that I can annotate MessageDestination as a #MappedSuperclass, but this can't work because it needs to be an #Entity to be stored in Message. Sadly, it's not possible:
org.hibernate.AnnotationException: An entity cannot be annotated with both #Entity and #MappedSuperclass
Any ideas? Thanks...
Since you are using table per class inheritance strategy and you dont have any mapped superclass (so each entity must have its own id).
You can annonate your MessageDestination Entity as #MappedSuperClass and remove the #Entity from MessageDestination. As by default its each subclass will inherited all its field including the #Id field
Pending for a better answer because the only solution I found is quite ugly. That consists of splitting the primary and the foreign key, so there is redundancy...
This:
#Entity #Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class MessageDestination implements Serializable {
#Id #OneToOne(cascade=CascadeType.ALL)
private Message msg;
}
public interface MessageDestinationRoomDAO
extends JpaRepository<MessageDestinationRoom, Message> {
public Set<MessageDestinationRoom> findMessageDestinationRoomByDestRoom
(Room dest);
}
becomes this:
#Entity #Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
abstract class MessageDestination implements Serializable {
#Id #GeneratedValue(strategy = GenerationType.TABLE)
private Long id;
#OneToOne(cascade=CascadeType.ALL)
private Message msg;
}
interface MessageDestinationRoomDAO
extends JpaRepository<MessageDestinationRoom, Long> {
public Set<MessageDestinationRoom> findMessageDestinationRoomByDestRoom
(Room dest);
}
I was also getting same issue when I was using #oneToMany And #ManyToOne Annotation based Mapping.
Basically what I was doing mistake was in the class that was throwing the error "does not define an IdClass" was having composite Keys i.e More that one #Id annotation used over two member variables due to which it was getting considered as Composite Key and since hibernate expects a seperate Key class needs to be defined in case of composite key this failure was coming.
I have a problem, I have two entity Job and JobPK
Job class looks like this sample code :
#Entity
#IdClass(JobPK.class)
#Table(name="JOB")
#Inheritance
#DiscriminatorColumn(name="JOB_TYPE")
public abstract class Job implements Serializable {
#Id
#Column(name="FOLDER_ID")
private BigDecimal folderId;
#Id
#ColumnDefinition(position = 1)
private String name;
#Column(name="JOB_TYPE",insertable=false,updatable=false)
private String jobType;
...
}
and JobPk :
public class JobPK implements Serializable {
private static final long serialVersionUID = -3266336718203527905L;
#Column(name="JOB_TYPE",insertable=false,updatable=false)
private String jobType;
#Id
private String name;
#Id
#Column(name="FOLDER_ID")
private BigDecimal folderId;
......
}
I have two class which extends Job : CalculatingJob and ImportingJob
Now I wont to use :
getEntityManager().find(CalculatingJob.class, new JobPK (BigDecimal.valueOf(folderId),name))
and I have problem because I must fill i JobPK descriminator value field. If I don't do that I've got Null Pointer Exception. Descriminator value is in key by default I think but I don't want put information about descriminator value explicite during JobPk creating. I thought that Entity which extends from Job will fill this field automaticaly. Any Idea to bypass this problem, maybe I can get Annotation #DescriminatorVale from CalculatingJob and then put into constructor JobPk
Thanks for Help
Try this configuration for Hierarchy structure
Job.java
#Table(name = "JOB")
#Inheritance
#IdClass(JobPK.class)
#DiscriminatorColumn(name = "JOB_TYPE", discriminatorType = DiscriminatorType.STRING)
public abstract class Job implements java.io.Serializable {
}
CalculatingJob.java
#Entity
#DiscriminatorValue("CalculatingJob")
public class CalculatingJob extends Job {
}
ImportingJob.java
#Entity
#DiscriminatorValue("ImportingJob")
public class ImportingJob extends Job {
}
JobPK.java
public class JobPK implements Serializable {
}
The discriminator value is entered by hibernate.
I have 4 persistent classes which all have the same fields (exactly) the only 3 difference between them is 1) the class name, 2) the table name and 3) the data. i am aware that this might seem strange to some but trust me there is a good reason which i won't go into here.
now, i'm using hibernate annotations to configure my class which should work like so:
#Entity
#Table(name = "store")
public class Store
{
#Id
#Column(name = "unique_id")
protected String id;
#Column
protected String category;
...
}
.. and this does work, for a single stand-alone class, however there are many fields to map and i'd like to do it all in one hit for all four similar classes, ie:
public class StoreBase
{
#Id
#Column(name = "unique_id")
protected String id;
#Column
protected String category;
...
}
#Entity
#Table(name = "store1")
public class Store1 extends StoreBase
{}
#Entity
#Table(name = "store2")
public class Store2 extends StoreBase
{}
#Entity
#Table(name = "store3")
public class Store3 extends StoreBase
{}
#Entity
#Table(name = "store4")
public class Store4 extends StoreBase
{}
however when attempting this i get the following exception:
Caused by: org.hibernate.AnnotationException: No identifier specified for entity: package.entities.Store1
at org.hibernate.cfg.AnnotationBinder.bindClass(AnnotationBinder.java:672)
at org.hibernate.cfg.AnnotationConfiguration.processArtifactsOfType(AnnotationConfiguration.java:546)
at org.hibernate.cfg.AnnotationConfiguration.secondPassCompile(AnnotationConfiguration.java:291)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1292)
at org.hibernate.cfg.AnnotationConfiguration.buildSessionFactory(AnnotationConfiguration.java:867)
i'm guessing this is because the super class is not being searched for the identifier?
is there a way to utilise inheritance in this context?
thanks, paul.
#MappedSuperclass
public class StoreBase
See docs for more info.
Have a look at #MappedSuperclass.