To be specific, I have these codes in Java (Class A and class B):
//Class A
package models;
import java.util.*;
import javax.persistence.*;
import play.db.ebean.Model;
#Entity
#Table(name="A")
public class A extends Model {
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="id_a_seq")
#SequenceGenerator(name="id_a_seq", sequenceName="id_a_seq")
#Column(name="ID_A", insertable=true, updatable=true, unique=true, nullable=false)
public Integer id;
#Column(name="NOME")
public String nome;
#Column(unique = true, name="CPF")
public String cpf;
#ManyToMany(cascade = {CascadeType.ALL})
#JoinTable(name="A_B",
joinColumns={#JoinColumn(name="ID_A")},
inverseJoinColumns={#JoinColumn(name="ID_B")})
public List<B> lista = new ArrayList<B>();
}
//Class B
package models;
import java.util.*;
import javax.persistence.*;
import play.db.ebean.Model;
#Entity
#Table(name="B")
public class B extends Model {
public B() {}
public B(Integer id, String nome) {
this.id = id;
this.nome = nome;
}
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="id_b_seq")
#SequenceGenerator(name="id_b_seq", sequenceName="id_b_seq")
//#AttributeOverride(name = "id", column = #Column(name = "ID_B"))
#Column(name="ID_B", insertable=true, updatable=true, unique=true, nullable=false)
public Integer id;
#Column(name="NOME")
public String nome;
#ManyToMany(mappedBy="lista")
public List<A> lista = new ArrayList<A>();
}
When I run localhost:9000 in the browser, I get this error message:
"PersistenceException: Error with the Join on [models.B.lista]. Could not find the local match for [ID_B] Perhaps an error in a #JoinColumn".
I could solve this problem renaming ID_A and ID_B to ID (for both), but I can't have the name ID for the id columns (in table A and in table B). I need to figure out how to make Play to accept the ID_A and ID_B names for their id column name.
Change the mapping on your lista relationship to:
#ManyToMany(cascade = {CascadeType.ALL})
#JoinTable(name = "A_B",
joinColumns = {#JoinColumn(name = "ID_A", referencedColumnName = "ID_A")},
inverseJoinColumns = {#JoinColumn(name = "ID_B", referencedColumnName = "ID_B")})
public List<B> lista = new ArrayList<B>();
Note the addition of referencedColumnName to #JoinColumn.
referencedColumnName reference:
When used inside a JoinTable annotation, the referenced key column is in the entity table of the owning entity, or inverse entity if the join is part of the inverse join definition.
Related
I am using the JPA Buddy plugin in IntelliJ, and I want to quickly create sql script (MySQL) from the entity class of Student and Course (many-to-many relationship).
I have successfully created the course and student table, but look like JPA Buddy does not have the option to create the associative (junction) table "student_course" (as showed in picture below)? Can JPA Buddy create the associative (junction) table in many-to-many relationship?
Student and Course class for code references:
Student.java
#Table(name = "student")
#Entity
#Getter
#Setter
public class Student {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Long id;
#Column(name = "name")
private String name;
#ManyToMany
#JoinTable(name = "student_course",
joinColumns = #JoinColumn(name = "student_id"),
inverseJoinColumns = #JoinColumn(name = "courses_id"))
private Set<Course> courses;
}
Course.java
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.util.Set;
#Table(name = "course")
#Entity
#Getter
#Setter
public class Course {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Long id;
#Column(name = "name")
private String name;
#ManyToMany(mappedBy = "courses")
private Set<Student> students;
}
Thanks for reporting! JPAB-1448 и JPAB-1469 tickets were closed and it works since the 2022.1.0 version of JPA Buddy
I have an application in which there are Projects associated with multiple Text entities and Text entities are associated with multiple projects (Many-To-Many).
Now when I try to fetch Text entities in hibernate I get projects=null
Here is the Project Side of the relationship:
import lombok.Data;
import javax.persistence.*;
import java.util.List;
#Data
#Entity
#Table(name = "project_data")
public class Project {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name="project_id")
private int projectId;
#Column(name="project_name")
private String projectName;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinTable(name="project_text", joinColumns = {#JoinColumn(name = "project_id")}, inverseJoinColumns = {#JoinColumn(name="text_id")})
private List<Text> texts;
public Project(String projectName){
this.projectName = projectName;
}
}
Here is the Text Side of the Relationship:
import lombok.Data;
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*;
import java.util.List;
#Data
#Entity
#Table(name="text_data")
public class Text {
#Id
#GeneratedValue(generator = "text_hash")
#GenericGenerator(name = "text_hash",strategy = "TextHashGenerator")
#Column(name="text_id")
private String text_id;
#Column(name="text_value")
private String text;
#ManyToMany(cascade = CascadeType.ALL, mappedBy = "texts", fetch = FetchType.LAZY)
private List<Project> projects;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name="text_keyword", joinColumns = {#JoinColumn(name = "text_id")}, inverseJoinColumns = {#JoinColumn(name = "keyword_id")})
private List<Keyword> keywords;
public Text(String text){
this.text = text;
}
}
Here is the custom TextHashGenerator I am using for generating ID for Text Entities
import org.apache.commons.codec.digest.DigestUtils;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.IdentifierGenerator;
import java.io.Serializable;
public class TextHashGenerator implements IdentifierGenerator{
#Override
public Serializable generate(SharedSessionContractImplementor sharedSessionContractImplementor, Object o) throws HibernateException {
String textHash = "";
if(o instanceof Text){
Text text = (Text) o;
textHash = DigestUtils.sha256Hex(text.getText());
}
return textHash;
}
}
this problem is with all the relationships as they are unable to fetch Owner side of the relationship.
Output
Text(text_id=21e57b707ffe2bd2d89b2b6f6c999597dc6e9dd90eaee809fbd06c222cf54de8, text=Text 1, projects=null, keywords=[Keyword(keywordId=2, keyword=Text 1 Keyword 1, texts=null, meanings=[Meaning(meaningId=3, meaning=Text 1 Keyword 1 meaning 1, keyword=null), Meaning(meaningId=4, meaning=Text 1 Keyword 1 meaning 2, keyword=null), Meaning(meaningId=5, meaning=Text 1 Keyword 2 meaning 1, keyword=null)]), Keyword(keywordId=6, keyword=Text 1 Keyword 2, texts=null, meanings=null), Keyword(keywordId=7, keyword=Text 2 Keyword 1, texts=null, meanings=null), Keyword(keywordId=8, keyword=Text 2 Keyword 2, texts=null, meanings=null)])
I am using following query to fetch Text
Query query = session.createQuery("from Text");
You have to join the relations if you set fetch = FetchType.LAZY (which you should, don't change that). Do it like this:
Query query = session.createQuery("SELECT t from Text t JOIN t.projects")
I want to join only one column from another table.
I have 2 entities now:
#Entity
public class Message {
....
#ManyToOne
#JoinColumn(name = "ATTRIBUTE_ID")
private Attribute attribute;
}
#Entity
#Table(name = "ATTRIBUTE_TABLE")
public class Attribute {
#Id
#Column(name = "ID")
private Long id;
#Column(name = "NAME")
private String name;
}
And I want to simplify code and don't use entity for only one column:
#Entity
#SecondaryTable(name = "ATTRIBUTE_TABLE", pkJoinColumns =
#PrimaryKeyJoinColumn(name = "ID", referencedColumnName = "ATTRIBUTE_ID")),
public class Message {
....
#Column(table = "ATTRIBUTE_TABLE", name = "NAME")
private String attribute;
}
But #SecondaryTable JoinColumn cannot reference a non-primary key.
How to add a column from another table without using additional entity for it?
I have four class; UserGroup, UserAccount, Role, UserGroupRoleRelation and my db is IBM DB2
#Entity
#Table(name = "USER_GROUP")
public class UserGroup implements Serializable {
#Id
#Column(name = "USER_GROUP_ID")
#GeneratedValue
private Long id;
......
..
#OneToMany(mappedBy = "userGroup", cascade = CascadeType.ALL, orphanRemoval = true)
private List<UserGroupRoleRelation> userAccountsRole = new ArrayList<UserGroupRoleRelation>();
}
#Entity
#Table(name = "ROLE")
public class Role implements Serializable {
#Id
#Column(name = "ROLE_ID")
#GeneratedValue
private Long id;
......
#OneToMany(mappedBy = "role")
private List<UserGroupRoleRelation> userAccountInGroup = new ArrayList<UserGroupRoleRelation>();
}
#Entity
#Table(name = "USER_GROUP_ROLE_LINE", uniqueConstraints = #UniqueConstraint(columnNames = { "ROLE_ID", "USER_GROUP_ID" }))
public class UserGroupRoleRelation {
#Id
#GeneratedValue
#Column(name = "RELATION_ID")
private Long relationId;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "USER_ACCOUNT_USER_GROUP_ROLE_LINE", joinColumns = { #JoinColumn(name = "RELATION_ID") }, inverseJoinColumns = { #JoinColumn(name = "USER_ID") }, uniqueConstraints = #UniqueConstraint(columnNames = { "USER_ID", "RELATION_ID" }))
private List<UserAccount> userAccountList = new ArrayList<UserAccount>();
#ManyToOne
#JoinColumn(name = "USER_GROUP_ID")
private UserGroup userGroup;
#ManyToOne
#JoinColumn(name = "ROLE_ID")
private Role role;
}
#Entity
#Table(name = "USER_ACCOUNT")
public class UserAccount implements Serializable {
#Id
#Column(name = "USER_ID")
#GeneratedValue
private Long id;
.....
#ManyToMany(mappedBy = "userAccountList", cascade = CascadeType.ALL)
private List<UserGroupRoleRelation> rolesInGroup = new ArrayList<UserGroupRoleRelation>();
}
I wanna find usergroups of a useraccount and i prepared a method with criteria. its like;
#Override
#Transactional
public List<UserGroup> findUserGroupOf(UserAccount userAccount) {
Criteria criteria = getSession().createCriteria(UserGroup.class);
criteria.createAlias("userAccountsRole", "userAccountsRole");
criteria.add(Restrictions.eq("userAccountsRole.userAccountList", userAccount));
return criteria.list();
}
But when i try to get result of that method, DB2 gives to me DB2 SQL Error: SQLCODE=-313, SQLSTATE=07004, SQLERRMC=null, DRIVER=3.63.75
Probably its about creating alias on many to many relation. I dont know what should i do to create alias on many to many. How can I get result of that function?
Thank
#Override
#Transactional
public List<UserGroup> findUserGroupOf(UserAccount userAccount) {
Criteria criteria = getSession().createCriteria(UserGroup.class);
criteria.createAlias("userAccountsRole", "userAccountsRole");
criteria.createAlias("userAccountsRole.userAccountList", "userAccountList");
criteria.add(Restrictions.eq("userAccountList.id", userAccount.getId()));
return criteria.list();
}
It works for me. I mean criteria on "id". But I don't understand why I cant check equality on object instead of id when there is ManyToMany list
It is not of creating alias. You are passing an object to hibernate on which it can not make any criteria. You need to create bidirectional mapping for that.Or else if you your requirement is just to fetch the the list of UserAccountList of particular UserGroup class you can follow the below code.
#Override
#Transactional
public List<UserGroup> findUserGroupOf(long userGroupId) {
Criteria criteria = getSession().createCriteria(UserGroup.class);
criteria.add(Restrictions.eq("id",userGroupId));
criteria.createAlias("userAccountsRole", "uar");
criteria.setFetchMode("uar.userAccountList",FetchMode.JOIN);
return criteria.list();
}
I need to create a join table in my database using JPA annotations so the result will be this:
So far I just implemented 2 entities:
#Entity
#Table(name="USERS", schema="ADMIN")
public class User implements Serializable {
private static final long serialVersionUID = -1244856316278032177L;
#Id
#Column(nullable = false)
private String userid;
#Column(nullable = false)
private String password;
public String getUserid() {
return userid;
}
public void setUserid(String userid) {
this.userid = userid;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
#Entity
#Table(name="GROUPS", schema="ADMIN")
public class Group implements Serializable {
private static final long serialVersionUID = -7274308564659753174L;
#Id
#Column(nullable = false)
private String groupid;
public String getGroupid() {
return groupid;
}
public void setGroupid(String groupid) {
this.groupid = groupid;
}
}
Should i create another entity called USER_GROUP or i can just add some annotations, so the join table will be created automatically when i run create tables from entities(ORM)?
How should i annotate my entities to achieve the same as in the image?
You definitely shouldn't create User_Group entity as it's more the underlying database representation than the object oriented one.
You can achieve the join table by defining something like:
#Entity
#Table(name="USERS", schema="ADMIN")
public class User implements Serializable {
//...
#ManyToOne
#JoinTable(name="USER_GROUP")
Group group;
#Entity
#Table(name="GROUPS", schema="ADMIN")
public class Group implements Serializable {
//...
#OneToMany(mappedBy="group")
Set<User> users;
Edit: If you want to explicitly set the names of the columns you could use #JoinColumn elements as shown below:
#ManyToOne
#JoinTable(name="USER_GROUP",
joinColumns = #JoinColumn(name = "userid",
referencedColumnName = "userid"),
inverseJoinColumns = #JoinColumn(name = "groupid",
referencedColumnName = "groupid"))
Group group;
I would implement it this way:
#Entity
#Table(name="GROUPS", schema="ADMIN")
public class Group implements Serializable {
#OneToMany
#JoinTable(name = "USER_GROUP",
joinColumns = #JoinColumn(name = "groupid"),
inverseJoinColumns = #JoinColumn(name = "userid"))
private List<User> users;
}
Solution suggested by #PedroKowalski should work too, but then you'll have to keep a reference to Group entity in your User entity which is not always possible.
To have the same annotations like in your diagram you can do this in your User class:
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "USER_GROUP",
joinColumns = { #JoinColumn(name = "userid") },
inverseJoinColumns = { #JoinColumn(name = "groupid") })
private List<Group> grups;
in your group class
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "USER_GROUP",
joinColumns = { #JoinColumn(name = "groupid") },
inverseJoinColumns = { #JoinColumn(name = "userid") })
private List<User> users;
I'm wondering what is the point to create a Join Table in this way, considering that we can't access directly for queries?
JPA doesn't allow to make queries directly to the Join Table, so if the user want to do an operation on USER_GROUP, he has to creare a normal join query between users and groups; due to this, the join table USER_GROUP is useless.