I've faced with an issue, I've declared the bidirectional #ManyToMany relation between my entities, but when I try to perform the select by repository, the #ManyToMany collection is always empty.
There is an example of my entities:
#Data
#EqualsAndHashCode
#Entity
#Table(name = "provider", schema = "debt")
public class ProviderEntity {
#Id
#Column(name = "provider_id")
private String providerId;
#Column(name = "external_provider_id")
private String externalProviderId;
private String description;
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(
name = "provider_supported_driver", schema = "debt",
joinColumns = #JoinColumn(name = "provider_id", foreignKey = #ForeignKey(name = "FK_PSD_TO_P")),
inverseJoinColumns = #JoinColumn(name = "driver_id", foreignKey = #ForeignKey(name = "FK_PSD_TO_D"))
)
private Set<DriverEntity> drivers = new HashSet<>();
}
#Data
#EqualsAndHashCode
#Entity
#Table(name = "driver", schema = "debt")
public class DriverEntity {
#Id
#Column(name = "driver_id")
private String driverId;
#Enumerated(EnumType.STRING)
private DriverType type;
private String description;
#ManyToMany(mappedBy = "drivers")
private Set<ProviderEntity> provider = new HashSet<>();
}
Also, I've enabled the SQL query logs, that hibernate generates, when I run it, the query works well.
I've tried different guides and answers for the same issues, but I've no result with them.
What's the reason for this behavior? Maybe has somebody any ideas?
I've found the reason for this issue and the problem is in the primary keys of my tables, they aren't generated by sequence or some different way, as I understand Hibernate doesn't understand how to map this columns
Related
I have two jpa entities User + profile with many to many relationship like below :
User Entity
#Data
#Entity
#AllArgsConstructor
#NoArgsConstructor
#Table(name = "t_user")
public class UserEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "user_id")
private Long Id;
#Column(nullable = false)
private String username;
#Column(nullable = false, length = 14)
private String siret;
#Cascade({
org.hibernate.annotations.CascadeType.SAVE_UPDATE,
org.hibernate.annotations.CascadeType.MERGE,
org.hibernate.annotations.CascadeType.PERSIST
})
#ManyToMany(fetch = FetchType.LAZY,cascade = CascadeType.MERGE)
#JoinTable(name = "t_user_profile", joinColumns = {#JoinColumn(name = "user_id")},
inverseJoinColumns = {#JoinColumn(name = "profile_id")})
private Set<ProfileEntity> profiles = new HashSet<>();
#OneToOne(fetch = FetchType.LAZY,cascade = CascadeType.ALL)
#JoinColumn(name = "ressource_id")
private RessourceEntity ressource;
}
and Profile entity :
#Data
#Entity
#AllArgsConstructor
#NoArgsConstructor
#Table(name = "t_profile")
public class ProfileEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "profile_id")
private Long Id;
#Column(nullable = false)
#Enumerated(EnumType.STRING)
private Profiles profile;
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "profiles")
private Set<UserEntity> user = new HashSet<>();
}
The problem is when I send an http request contaning the same profiles with different users, I obtain new duplicated profiles records with new IDs in the Profile table in the database like below :
but the correct way is to add the informations ONLY in the joined table t_user_profile because profile already exists in profile table .
I tried cascade.All , persist and merge .... but the same result .
And if I remove cascade and i search for profile Id using findById and get the Id and insert... I got a problem when the profile is new and don't exist in DB and the problem is because I removed cascade ... So I want if the profile already exists , I want to add the relationship in the joined table ONLY.
I have two entities connected with many-to-many relationship. For example:
#Entity
public class Account {
#Id
private Long id;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(
name = "account_games",
joinColumns = {#JoinColumn(name="account_id")},
inverseJoinColumns = {#JoinColumn(name="game_id")}
)
private Set<Game> games = new HashSet<>();
}
#Entity
public class Game {
#Id
private Long id;
#ManyToMany(mappedBy = "games", fetch = FetchType.LAZY)
List<Account> accounts = new ArrayList<>();
}
So, there is a table account_games(account_id, game_id) in mysql describing entities many-to-many relations.
I don't want to have Game entity anymore. Is there a way to get rid of Game and leave gameId relation only? So, I'd like to have code something like that:
#Entity
public class Account {
#Id
private Long id;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(
name = "account_games",
joinColumns = {#JoinColumn(name="account_id")},
inverseJoinColumns = {#JoinColumn(name="game_id")}
)
private Set<Long> gameIds = new HashSet<>();
}
without making changes in database.
I've tried different configuration on javax.persistance annotations, but none worked
You can use #ElementCollection and #CollectionTable to achieve that.
#Entity
public class Account {
#Id
private Long id;
#ElementCollection(fetch = FetchType.LAZY)
#CollectionTable(name = "account_games", joinColumns = #JoinColumn(name = "account_id"))
#Column(name = "game_id", nullable = false)
private Set<Long> gameIds = new HashSet<>();
}
You may have to change the query on how to filter data using gameId. Element Collection Query
I have a problem.
I am writing program which is connecting to database, where I cant change anything. I can only read data from it. So let's say I have three tables MOVIES, BOOKS, REVIEWDOCUMENT and two many to many tables MOVIES_REVIEWDOCUMENT plus BOOKS_REVIEWDOCUMENT.
Because I am using Spring Boot with Hibernate, I have written simple entities classes.
#Entity(name = "MOVIES")
#Table(name = "MOVIES")
public class Movies {
#Id
#Column(name = "ID")
private Long id;
#Column(name = "MOVIE_KEY")
private String movieKey;
#Column(name = "TYPE_ANIMATED")
private String typeAnim;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "MOVIES_REVIEWDOCUMENTS",
joinColumns = #JoinColumn(name = "MOVIE_KEY"),
inverseJoinColumns = #JoinColumn(name = "DOCUMENT_KEY"))
private List<ReviewDocuments> reviewDocuments;
}
#Entity(name = "BOOKS")
#Table(name = "BOOKS")
public class Books {
#Id
#Column(name = "ID")
private Long id;
#Column(name = "BOOK_KEY")
private String bookKey;
#Column(name = "TYPE_WRITTEN")
private String typeWritten;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "BOOKS_REVIEWDOCUMENTS",
joinColumns = #JoinColumn(name = "BOOK_KEY"),
inverseJoinColumns = #JoinColumn(name = "DOCUMENT_KEY"))
private List<ReviewDocuments> reviewDocuments;
}
#Entity(name = "REVIEWDOCUMENTS")
#Table(name = "REVIEWDOCUMENTS")
public class ReviewDocuments {
#Id
#Column(name = "OBJID")
private Long objId;
#ManyToMany(mappedBy = "reviewDocuments")
private Set<Movies> movies;
#ManyToMany(mappedBy = "reviewDocuments")
private Set<Books> books;
}
And it is working pretty ok. But because as you can see MOVIES and BOOKS are almost indentical, only diffrence are those relational tables. I was wondering if I can somehow extract it to abstrac class.
Because what I need to do is to create service class which will iterate after books/movies and it's documents and do some operation on reviewDocuments. So easies way would be creating generic service for each of entities.
Example:
public void extractData() throws IOException {
Path tempDirectory = Files.createTempDirectory("zip_");
tempDirectory.toFile().deleteOnExit();
Set<Book> books = movieRepository.findByRidKey(extractionParameters.getWriteNumber());
for (Book book :
books) {
for (ReviewDocuments documents :
book.getReviewDocuments()) {
exportDataToFile(data);
}
directoryToZip(tempDirectory.toFile(), book.getId());
FileUtils.cleanDirectory(tempDirectory.toFile());
}
FileUtils.deleteDirectory(tempDirectory.toFile());
}
I don‘t think you can use inheritance with multiple many-to-many tables.
You could however define a common interface and implement your service based on that interface.
In a database there are "pastries". A pastry has zero, one, or many allergens.
Hence I'm using a list in Pastry with #OneToMany relation. Now, with this way of managing the relations, we get redundant entries, as for each pastry a new entry with the same allergen is created:
Our current solution looks like this:
A mapping-table in between those two database tables.
How can I achieve a non-redundant solution with direct references to the allergen elements from a list in the Pastry class? I googled a lot but sadly couldn't find anything helpful.
Would look like this:
Regarding my comment on solution:
What you need is a called a JoinTable. Based on your schema definition this could work as a potential example:
#Data
#Entity(name = "Pastry")
#Table(name = "PASTRY")
public class Pastry {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#ManyToMany
#JoinTable(
name = "PASTRY_ALLERGEN",
foreignKey = #ForeignKey(name = "FK_PASTRY_ALLERGEN_PASTRY"),
inverseForeignKey = #ForeignKey(name = "FK_PASTRY_ALLERGEN_ALLERGEN"),
joinColumns = #JoinColumn(name = "pastry_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "allergen_id", referencedColumnName = "id")
)
private Set<Allergen> allergens = new HashSet<>();
}
For the Pastry association and:
#Data
#Entity(name = "Allergen")
#Table(name = "ALLERGEN")
public class Allergen {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
}
For the Allergen association.
This mapping will in turn produce an intermediate table called PastryAllergens which will hold reference to the PK of the Pastry table and the PK of the Allergen table.
I have a parent class:
#MappedSuperclass
public class BaseText implements Serializable {
#Column(name = "LOCALE_CODE", nullable = false)
private String localeCode;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#Index
#Column(name = "LOCALIZED_TEXT_ID", nullable = false)
#ForeignKey
private LocalizedText localizedText;
//getters and setters
}
And one of the sub classes:
#Entity
#Table(name = "ASSESSMENT_TEXT")
#AttributeOverride(name = "localeCode", column = #Column(name = "LOCALE_CODE"))
#AssociationOverride(name = "localizedText", joinColumns = #JoinColumn(name = "LOCALIZED_TEXT_ID"))
public class AssessmentText extends BaseText {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private Long id;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#Index
#Column(name = "ASSESSMENT_ID", nullable = false)
#ForeignKey
private Assessment assessment;
//Getters and setters.
}
When I am trying to persist an object I get the following error:
org.apache.openjpa.persistence.ArgumentException: Superclass field "java.lang.Object.localizedText" is mapped in the metadata for subclass "com.my.com.AssessmentText", but is not a persistent field.
What is causing this and how to solve it?
I am using embedded derby database in JUnit and the JPA implementation is OpenJPA.
I am not sure that it is the solution, but I miss an #Inheritance annotation from AssessmentText:
#Entity
#Table(name = "ASSESSMENT_TEXT")
#Inheritance(strategy=InheritanceType.JOINED)
#AttributeOverride(name = "localeCode", column = #Column(name = "LOCALE_CODE"))
#AssociationOverride(name = "localizedText",
joinColumns = #JoinColumn(name = "LOCALIZED_TEXT_ID"))
public class AssessmentText extends BaseText {
I had the same issue, and solved it by adding the MappedSuperClass to the persistence.xml.
I know this is also in the comments somewhere, but the useful one was hidden and I think this should be an answer, not a comment to the question.