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();
}
Related
I got a lot of answers and advice, tried changing my code according to these tips.
But, unfortunately, these tips helped only partially.
Now, when creating a new project and creating a new user, I can add the desired user to the set of projects, and the required project will be added to the set of users.
But the relationship between the desired project and the desired user will not appear in the project_user table.
Please help find the answer.
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, cascade = CascadeType.ALL)
#JoinTable(name = "project_users",
joinColumns = #JoinColumn(name = "project_id"),
inverseJoinColumns = #JoinColumn(name = "users_id"))
private Set<User> projectUserSet = new HashSet<>();
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "project_task",
joinColumns = #JoinColumn(name = "project_id"),
inverseJoinColumns = #JoinColumn(name = "task_id"))
private Set<Task> projectTaskSet = new HashSet<>();
public void addUserToProject(User user){
this.projectUserSet.add(user);
user.getUserProjectsSet().add(this);
}
public void addTasksToProject(Task task){
this.projectTaskSet.add(task);
task.getTasksProjectSet().add(this);
}
//constructors, hashCode, equals, toString
}
Entity User
#Data
#Entity
#AllArgsConstructor
#NoArgsConstructor
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column
private String firstName;
#Column
private String lastName;
#ManyToMany(mappedBy = "projectUserSet", cascade = CascadeType.ALL)
private Set<Project> userProjectsSet = new HashSet<>();
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "users_task",
joinColumns = {#JoinColumn(name = "users_id")},
inverseJoinColumns = {#JoinColumn(name = "task_id")})
private Set<Task> userTasksSet = new HashSet<>();
public void addTaskToUser(Task task) {
this.userTasksSet.add(task);
task.getTasksUserSet().add(this);
}
//constructors, hashCode, equals, toString
}
project and user initialization
Project project1 = new Project("Project1", "Project1");
User user1 = new User("User1", "User1");
project1.addUserToProject(user1);
With code shown below, table project_user is populated, verified using H2 console. In order to avoid stack overflow, I had to modify method Project#addTaskToUser as shown below.
Please note that only code relevant to the question, is included.
Normally, issue should be described by some tests. In this case, I added a CommandLineRunner that runs at startup.
CascadeType.ALL is not recommended for many-to-many relations, hence I changed this in code shown below.
Tested using H2 in-memory db.
Project class
#Data
#Entity
public class Project {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
#ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinTable(name = "project_users",
joinColumns = #JoinColumn(name = "project_id"),
inverseJoinColumns = #JoinColumn(name = "users_id"))
private Set<MyUser> projectUserSet = new HashSet<>();
public void addUserToProject(User user) {
this.projectUserSet.add(user);
}
}
Project repo
public interface ProjectRepo extends JpaRepository<Project, Long> { }
User class
// cannot use #Data here because it will cause cyclic ref and stack overflow when accessing userProjectsSet
#Getter
#Setter
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
#ManyToMany(mappedBy="projectUserSet")
private Set<Project> userProjectsSet = new HashSet<>();
}
User repo
public interface UserRepo extends JpaRepository<User, Long> {}
In app class
#SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
#Bean
CommandLineRunner run(ProjectRepo projectRepo, EntityManagerFactory entityManagerFactory) {
return args -> {
var testUser = new User();
testUser.setFirstName("first-name");
testUser.setLastName("last-name");
var project = new Project();
project.setName("project-name");
project.setDescription("project-description");
project.addUserToProject(testUser);
projectRepo.save(project);
// get saved user and print some properties
var userInDb = entityManagerFactory.createEntityManager().find(User.class, testUser.getId());
System.out.println(userInDb.getFirstName()); // prints "first-name"
System.out.println(userInDb.getUserProjectsSet().size()); // prints "1"
};
}
}
I'm making an app that holds a UserProfile with Wallet that has many Transactions.
Here's the Transaction class:
#Entity
#Table(name = "transactions")
public class Transaction extends BaseEntity {
#Column(name = "amount", nullable = false)
private BigDecimal amount;
#Column(name = "executed_on", nullable = false)
private LocalDateTime executedOn;
#Column(name = "is_top_up")
private boolean isTopUp;
#Column(name = "note")
private String note;
#ManyToOne(targetEntity = UserProfile.class)
private UserProfile sender;
#ManyToOne(targetEntity = UserProfile.class)
private UserProfile receiver;
public Transaction() {
}
Here's the Wallet class
#Entity
#Table(name = "wallets")
public class Wallet extends BaseEntity {
#ManyToMany(targetEntity = Transaction.class, cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
}, fetch = FetchType.EAGER)
#JoinTable(name = "wallets_transactions",
joinColumns = #JoinColumn(name = "wallet_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "transaction_id", referencedColumnName = "id")
)
private Set<Transaction> transactions;
public Wallet() {
this.transactions = new HashSet<>();
}
public Set<Transaction> getTransactions() {
return transactions;
}
public void setTransactions(Set<Transaction> transactions) {
this.transactions = transactions;
}
public void addTransaction(Transaction transaction) {
this.transactions.add(transaction);
}
}
What I want is, to get all transactions by sender and receiver search criteria. For example, user 'A' sent money to user 'B'. I'm using JpaRepository. The end result should be in a Page<Transaction> class.
So far, when using just findAllBySender(UserProfile sender, Pageable pageable), it does work and I get the exact right Transactions. But when I try Page<Transaction> findAllBySenderAndReceiver(UserProfile sender, UserProfile receiver, Pageable pageable); I get a Page<T> with 0 elements when my DB has test data with at least 1 record.
Solved. Turns out, I had a logical error. Thank you all, for your assistance.
I am using Postgresql for my database and it contains a table called user and a table called friendship, which has 2 foreign keys userA_id and userB_id. I know how to use mappedBy to check for friendships based on userA_id but I am not sure how to check for userB_id. Is there a way to tell hibernate to check a user ID from user table with both of columns on friendship table?
EDIT: Here is the code I currently have.
#Entity
#Table(name = "users")
public class UserDB implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "userid", nullable=false)
public int userID; //not null
#OneToMany (targetEntity = FriendshipDB.class, mappedBy = "userA_ID", cascade = CascadeType.ALL, fetch=FetchType.EAGER)
//#OneToMany (targetEntity = FriendshipDB.class, mappedBy = "userB_ID", cascade = CascadeType.ALL, fetch=FetchType.EAGER)
public List<FriendshipDB> friends = new ArrayList<>();
}
#Entity
#Table(name = "friendships")
public class FriendshipDB implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "friendshipid", nullable = false)
private int friendshipID; //not null
#ManyToOne
#JoinColumn(name="usera_id")
private UserDB userA_ID; //not null
#ManyToOne
#JoinColumn(name = "userB_id")
private UserDB userB_ID;
}
I think this is very specific mapping but the only solution I know is to go with 2 association like this:
#OneToMany(mappedBy = "user1")
private Collection<User> usersByFirst;
#OneToMany(mappedBy = "user2")
private Collection<User> usersBySecond;
I need CompanyUser.companyRolCompanyUsers property with OneToMany relation, completed in each query.
JPA company_usr entity:
#Entity
#Table(name = "company_usr")
public class CompanyUser extends BaseModel implements Serializable {
#Id
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(nullable = false)
private Company company;
#Id
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "usr_id", nullable = false)
private User user;
#Column(nullable = false)
private Boolean external;
#OneToMany(fetch = FetchType.EAGER)
private List<CompanyRolCompanyUser> companyRolCompanyUsers;
....
JPA companyRol_companyUsr entity:
#Entity
#Table(name = "companyRol_companyUsr")
public class CompanyRolCompanyUser extends BaseModel implements Serializable {
#Id
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumns({
#JoinColumn(name="companyuser_company_id", referencedColumnName="company_id"),
#JoinColumn(name="companyuser_usr_id", referencedColumnName="usr_id")
})
private CompanyUser companyUser;
#Id
#ManyToOne(fetch = FetchType.EAGER)
private CompanyRol companyRol;
....
How to set mappedBy in companyRolCompanyUsers property correctly?
If i get what you want to achieve i think you need something like :
#OneToMany(fetch = FetchType.EAGER, mappedBy="companyUser")
private List<CompanyRolCompanyUser> companyRolCompanyUsers;
You can have two properties for the same column like this :
#JoinColumn(name = "CGRADO_CODIGO", referencedColumnName = "CGRADO_CODIGO")
#ManyToOne
#NotFound(action=NotFoundAction.IGNORE)
private SipreGrado sipreGrado;
#Column(name = "CGRADO_CODIGO",insertable=false,updatable=false)
private String sipreGradoCodigo;
Remember if you have sometimes the entity NULL you can skip it with that annotation
#NotFound(action=NotFoundAction.IGNORE)
Also,Remember to set
insertable=false,updatable=false
for the one that you dont want to include in the insert/update queries.
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.