I have three Entities i'm modeling and am having issues with the associated annotations. I basically have a class that I intend on returning to the caller, a nested listed of Project's and the Project can contain a nested list of Endpoint's. It's a top-level has-a one-to-many, then the nested one-to-many has two one-to-many's.
I've played with #JoinColumn annotations, i've attempted to put a #ManyToOne on the other side of the OneToMany's (but it doesn't like that it's a Long..). I'm just fairly new and unsure on how to do this. I think the mappedById is the solution, but i'm uncertain.
Main Issue: This code allows me to "save" to the database, but upon retrieval, the list of Project's inside the DownDetectorPackage is empty.
A CascadeType.ALL throws referential integrity errors that I don't completely understand.
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Builder
public class DownDetectorPackage {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#OneToMany(mappedBy="id",fetch = FetchType.EAGER)
private List<Project> projects;
#Temporal(TemporalType.DATE)
private Date dateJobsLastRan;
#Entity
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder
public class Project{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String projectName;
#OneToMany(mappedBy="id")
private List<Service> externalDependencies;
#OneToMany(mappedBy="id")
private List<Service> endpoints;
}
#Entity
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder
public class Service {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String url;
private Boolean endpointIsUp;
private String serviceName;
}
You should be using #JoinColumn instead of mappedBy. MappedBy can be used when you have used #ManyToOne in the other class too, which you haven't.
So your final class should look something like this (this is applicable for the other classes too which you have mentioned) :
public class DownDetectorPackage {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#JoinColumn(name = "downDetectorPackageId")
#OneToMany(fetch = FetchType.EAGER)
private List<Project> projects;
#Temporal(TemporalType.DATE)
private Date dateJobsLastRan;
Also, remember to state the parent object name in #JoinColumn annotation, since it would create a column for that foreign key.
You should mark every join column as JoinColumn denotating the referenced column from the other entity. Then, you are supposed to say which relation type are using this column.
public class Project {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String projectName;
#JoinColumn(referencedColumnName = "id")
#OneToMany(fetch = FetchType.LAZY)
private ExternalDependencyEntity externalDependencies;
#JoinColumn(referencedColumnName = "id")
#OneToMany(fetch = FetchType.LAZY)
private EndpointEntity endpoints;
}
Finally, note that in a relational database, every fk column can takes only 1 value (pk of referenced entity id), so, on your entity, you should mark the data type as the entity you are refering to and no as a collection.
I think this sould solve your problem.
Related
As in the title, when performing the update operation, the previous child loses the reference to the parent.
Parent side
#OneToMany(cascade =CascadeType.ALL)
#JoinColumn(name = "individual_id")
private List<ContactMedium> contactMedium;
Children side
#Entity
#Table(name = "contactMedium")
#Data
#AllArgsConstructor
#NoArgsConstructor
public class ContactMedium
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id ;
#ManyToOne
private Individual individual;
Patch operation
public Individual patch(Individual individual, Long id) {
Individual objectToSave = individual;
objectToSave.setId(id);
return individualRepository.save(objectToSave);
}
When updating, the previous property loses references to the child. How can I prevent this?
Your mappings seems wrong. Ideally they should be as below:
#Entity
#Table(name = "contactMedium")
#Data
#AllArgsConstructor
#NoArgsConstructor
public class ContactMedium
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id ;
#ManyToOne
#JoinColumn
private Individual individual;
and
#OneToMany(mappedBy = "individual", cascade = CascadeType.ALL)
private List<ContactMedium> contactMedium;
You need to save the ContactMedium and Individual will automatically be saved. Here ContactMedium has the foreign key reference to Individual (and that is what is depicted in your database table screenshot).
Often one use mappedBy as parameter to #OneToMany instead of #JoinColumn to make the relationship two-ways.
Can you please try to change
#OneToMany(cascade =CascadeType.ALL)
#JoinColumn(name = "individual_id")
private List<ContactMedium> contactMedium;
to
#OneToMany(mappedBy = "individual", cascade =CascadeType.ALL)
private List<ContactMedium> contactMedium;
and see if that worked better?
I think you must add the #OneToMany(mappedBy="individual" , cascade =CascadeType.PERSIST) and the #JoinColumn in the #ManyToOne as below:
#OneToMany(mappedBy = "individual", cascade =CascadeType.PERSIST)
private List<ContactMedium> contactMedium;
#ManyToOne
#JoinColumn(name = "individual_id")
private Individual individual;
You should retrieve the entity from the database using the ID first and then update the specific fields and persist the updated entity back.
At the moment I have 2 entities - Project and User. The Project object has a #ManyToMany relationship to the User object. After launching the application, I open the "project_user" table that created using "hibernate" and manually fill in the project_id and user_id columns to indicate which project is associated with which users.
But I need to use "ddl-auto: create-drop", and when I restart the application, this table has to be populated again.
Can this be done automatically?
Entity Project
#Data
#Entity
#AllArgsConstructor
#NoArgsConstructor
#Table(name = "project")
public class Project {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column
private String name;
#Column
private String description;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "project_users",
joinColumns = #JoinColumn(name = "project_id"),
inverseJoinColumns = #JoinColumn(name = "users_id"))
private Set<User> usersSet = new HashSet<>();
Entity User
#Data
#Entity
#AllArgsConstructor
#NoArgsConstructor
#Table(name = "users")
#JsonIgnoreProperties({"hibernateLazyInitializer"})
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column
private String firstName;
#Column
private String lastName;
Yes, you can use importing script files to customize the schema generation process, the hibernate.hbm2ddl.import_files configuration property must be used to provide other scripts files that Hibernate can use when the SessionFactory is started.
<property name="hibernate.hbm2ddl.import_files" value="schema-generation.sql" />
Hibernate is going to execute the script file after the schema is automatically generated.
#Entity
#Data
#NoArgsConstructor
public class Offer {
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name = "user_id", referencedColumnName = "id")
private User user;
}
#Data
#EqualsAndHashCode
#Entity
#NoArgsConstructor
public class User {
#OneToMany(mappedBy = "user",cascade = CascadeType.ALL,fetch=FetchType.LAZY)
private Set<Offer> offers = new HashSet<Offer>();
}
Please help if the mapping is correct in table User and Offer .user_id column have null values ....:(
I'm not sure if these are only parts of the entities but in order for the entity to have an id, you need to provide it with one and annotate the relevant field as #Id.
I also use #GeneratedValue(strategy = GenerationType.IDENTITY) so each table will get it's own id (generated by Hibernate, you don't provide the id when you save a new entity and not a global id, otherwise let's say you add an Offer, you get id with value x, then add new User you get id with value x+1 and so on...
#Entity
#Data
#NoArgsConstructor
public class Offer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name = "user_id", referencedColumnName = "id")
private User user;
}
#Data
#EqualsAndHashCode
#Entity
#NoArgsConstructor
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#OneToMany(mappedBy = "user",cascade = CascadeType.ALL,fetch=FetchType.LAZY)
private Set<Offer> offers = new HashSet<Offer>();
}`
I have a table PATIENT which has some fields. There's also a CONTACT table that has a field called 'patientId' that needs to store PATIENT's ID (which is autogenerated), and a PATIENT_CONTACT table that only relates the two tables.
Now, here comes the tricky part. There are three other tables: CONTACT_ADDRESS, CONTACT_PHONE, CONTACT_EMAIL. A row in CONTACT will have the same ID as one (and only one) of CONTACT_ADDRESS, CONTACT_PHONE and CONTACT EMAIL. How do I get this all to work?
I have tried so many approaches, this is what I have right now:
#Entity
#Table(name = "patient", schema = "patient")
public class PatientEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
//... more fields
#OneToOne
private ContactEmailEntity contactEmailEntity;
#OneToOne
private ContactAddressEntity contactAddressEntity;
#OneToOne
private ContactPhoneEntity contactPhoneEntity;
}
The three CONTACT_* classes are similar and they look like this:
#Table(name = "contact_address", schema = "patient")
public class ContactAddressEntity {
#Id
#Column(name = "id")
private Long id;
// ... more fields
#OneToOne(cascade = {CascadeType.ALL})
#MapsId
private ContactEntity contact;
}
And my CONTACT class looks like this:
#Table(name = "contacto", schema = "paciente")
public class ContactEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
//... more fields
Can you see things that don't look right or could be done better? I get all sorts of errors with every approach. My latest one is:
ERROR: column patientent0_.contact_address_entity_contact_id does not exist
when trying to do a simple patient find. Please, any help is appreciated!
#Entity
public class Blobx {
private String name;
private BlobKey blobKey;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key id;
//getters and setters
}
#Entity
public class Userx {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key id;
private String name;
#OneToMany
private List<Blobx> blobs;
//getters and setters
}
while persiting the above Userx entity object i am encountering
java.lang.IllegalStateException: Field "entities.Userx.blobs" contains a persistable object that isnt persistent, but the field doesnt allow cascade-persist!
I think you need to add a cascade attribute so that the JPA provider can cascade persist on the new Blobx added to the blobs. Currently, the JPA provider can't, as reported by the error message. So change it like this (adapt the CascadeType to match your needs):
#OneToMany(cascade = CascadeType.ALL)
private List<Blobx> blobs;