My JPA-entity classes look like this:
#Entity
#Table(name = "users")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private Long id;
...
// bi-directional many-to-many association to Tag
#ManyToMany
#JoinTable(name = "user_tags_preferences", joinColumns = {
#JoinColumn(name = "user_id") },
inverseJoinColumns = {
#JoinColumn(name = "tag_id") })
private List<Tag> tags;
#Entity
#Table(name = "tags")
public class Tag implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private Long id;
private String name;
...
// bi-directional many-to-many association to CookEvent
#ManyToMany(mappedBy = "tags")
private List<CookEvent> cookEvents;
// bi-directional many-to-many association to User
#ManyToMany(mappedBy = "tags")
private List<User> users;
#Entity
#Table(name = "cook_events")
public class CookEvent implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private Long id;
#Column(name = "takes_place_on")
private LocalDateTime takesPlaceOn;
...
// bi-directional many-to-many association to Tag
#ManyToMany
#JoinTable(name = "cook_events_tags", joinColumns = {
#JoinColumn(name = "cook_event_id") },
inverseJoinColumns = {
#JoinColumn(name = "tag_id") })
#OrderBy("name")
private List<Tag> tags;
In my database I thus have 'users', 'cook_events', 'tags', 'user_tags_preferences', 'cook_events_tags' tables.
I would need to make a JPQL query that does following:
From my front-end I have the user_id.
I would like a query that filters all cook_events that are takesPlaceOn > CURRENT_TIMESTAMP, and where at least one of the user_tags_preferences from my user_id matches the cook_event_tags.
Is there a possibility with joins to filter all this to ultimately just get a List<CookEvent> I need. Or even a List<Tag> if it's easier.
It can even be different queries and that I filter it myself in the backend, but I would like to try to filter everything within a query.
Could this just be the answer?
SELECT DISTINCT(c) from User u JOIN u.tags t JOIN t.cookEvents c
WHERE u.id = :id AND c.takesPlaceOn > CURRENT_TIMESTAMP
Step 1.
Replace List in all your many-to-many associations with Set.
Step 2.
Change the JPQL query to:
SELECT c from CookEvent c JOIN FETCH c.tags t JOIN FETCH t.users u WHERE c.takesPlaceOn >
CURRENT_TIMESTAMP And u.id = :id
Related
I have 2 Entities : User.java and PushNotification.java
and mapping table named as userpushnotification where many-to-many mapping takes place.
the existing data in userpushnotification table is a kind of important for me.
So if i try to add users(let's say id=5,6,7) for pushnotification(id=2), hibernate deletes the previous data for pushnotification(id=2) in relationship table and then it adds the new users for that pushnotification(id=2).
My need is to keep all the records in relationship table.
So how can i restrict the Hibernate/JPA to execute only insert queries intstead of executing delete and insert queries.
In simple words, I just want to append data in relationship table instead of overwriting.
User.java :-
#Entity
#Table(name = "user")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "id")
#GenericGenerator(name = "gen", strategy = "identity")
#GeneratedValue(generator = "gen")
private long id;
#Column(name = "username")
private String username;
#Column(name = "password")
private String password;
#Column(name = "authkey")
private String authKey;
#ManyToMany(fetch = FetchType.EAGER, mappedBy = "users")
private Set<PushNotifications> pushNotifications = new HashSet<PushNotifications>();
PushNotifications.java :-
#Entity
#Table(name = "pushnotifications", uniqueConstraints = #UniqueConstraint(columnNames = { "id" }))
public class PushNotifications implements Serializable {
#Id
#Column(name = "id")
#GenericGenerator(name="gen",strategy="identity")
#GeneratedValue(generator="gen")
private long id;
#Column(name = "shortdescription")
private String shortDescription;
#Column(name = "title")
private String title;
#JsonIgnore
#ManyToMany(cascade = { CascadeType.ALL }, fetch = FetchType.LAZY)
#JoinTable(name = "userpushnotifications",
joinColumns = {#JoinColumn(name = "pushnotificatoinId") },
inverseJoinColumns = { #JoinColumn(name = "userId") })
private Set<User> users = new HashSet<User>();
public Set<User> getUsers() {
return users;
}
public void setUsers(Set<User> users) {
this.users = users;
}
When i try to do this :
PushNotifications notifications = iNotificationService
.getNotification(notificationId);
Set<User> newUsers = new HashSet<User>();
newUsers .add(newUserToBeNotified_1);
newUsers .add(newUserToBeNotified_2);
notifications.setUsers(newUsers);
sessionFactory.getCurrentSession().merge(notifications);
Here, am tring to add 2 users for that notification type, Already there is one user for that notification type in relationship table.
Hibernate executing these queries :
Hibernate:
delete
from
userpushnotifications
where
pushnotificatoinId=?
and userId=?
Hibernate:
insert
into
userpushnotifications
(pushnotificatoinId, userId)
values
(?, ?)
Hibernate:
insert
into
userpushnotifications
(pushnotificatoinId, userId)
values
(?, ?)
So, i hope u got me, i dont want hibernate to make delete operations.
Please help me resolve this, Looking for answers...
Thanks in advance.
You could use Blaze-Persistence for this which works on top of JPA and provides support for DML operations for collections. A query could look like the following
criteriaBuilderFactory.insertCollection(entityManager, PushNotifications.class, "users")
.fromIdentifiableValues(User.class, "u", newUsers)
.bind("id", notification.getId())
.bind("users").select("u")
.executeUpdate();
After setting Blaze-Persistence up like described in the documentation you can create a repository like this:
#Component
class MyRepository {
#Autowired CriteriaBuilderFactory cbf;
#Autowired EntityManager em;
public void addUsers(Collection<User> newUsers) {
cbf.insertCollection(em, PushNotifications.class, "users")
.fromIdentifiableValues(User.class, "u", newUsers)
.bind("id", notification.getId())
.bind("users").select("u")
.executeUpdate();
}
}
This will issue just the insert into the join table.
I have a Controller from rest service that I call a Hibernate method to get the result, but I really don't know why the children components didn't come. When I call this method using Junit, It works.
This is the Code:
{
#Entity
public class Product implements Serializable {
private static final long serialVersionUID = -6131311050358241535L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(nullable = false)
private String name;
private String description;
#OneToMany(mappedBy = "product")
private List<Image> images = new ArrayList<Image>();
}
{
#Entity
public class Image implements Serializable {
private static final long serialVersionUID = 2128787860415180858L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#JoinColumn(name = "product_id")
#ManyToOne
private Product product;
private ImageType type;
}
{
#PersistenceContext
private EntityManager entityManager;
public List<Product> findAllWithParentProductsAndImage() {
String hpql = "select distinct p from Product p left join fetch p.images";
List<Product> resultList = entityManager.createQuery(hpql,
Product.class).getResultList();
return resultList;
}
}
By default #OneToMany will load lazily.
You should use #OneToMany( mappedBy = "product", fetch=FetchType.Eager ) to do Eager fetch
You can definitely use
#OneToMany(mappedBy = "product", fetch=FetchType.Eager)
However this has a downside. You will always be fetching children even if you only want the Parent and its few properties.
Use JOIN FETCH within your #Query if you are using JpaRepositories.
Check out the following related questions
How to properly express JPQL "join fetch" with "where" clause as JPA 2 CriteriaQuery?
http://www.objectdb.com/java/jpa/query/jpql/from#LEFT_OUTER_INNER_JOIN_FETCH_
https://stackoverflow.com/a/29667050/3094731
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 am trying to create a new User(entity1) - it has reference to a Group (entity2) via a link table Member (entity3)
A user has a Set of groups as a class variable.
When i create my user object i want to say this user will be a member of group n (there are pre defined users that are linked to by id (1,2,3,4,5,6...) each group has some associated data in the table.
Whenever I create my user object as follows;
User user = new User();
user.setActive(1);
user.setCrby("me");
user.setUsername("username");
user.setCrdate("2016-06-20 12:42:53.610");
user.setCrwsref("...");
user.setModby("...");
user.setModdate("2016-06-20 12:42:53.610");
user.setModswref("..");
user.setBackground("Y");
user.setPassword("password");
user.setFullName("me");
Group group = new Group();
group.setId(1);
Group group2 = new Group();
group2.setId(2);
Set<Group> sets = new HashSet<Group>();
sets.add(group);
sets.add(group2);
user.setGroups(sets);
userDao.addUser(user);
I keep getting errors telling me that certain columns cannot be null. What I actually want to happen here is not to be doing an insert in to the group table but associating a user to a line in the group table. Is there a particular way I can prevent the columns in the group table being modified? I think I need to modify the mappings between the link table - this is how much pojos link right now
User
#Entity
#Table(name = "user")
public class User
{
#Id
#GeneratedValue
#Column(name = "id")
private int id;
#Column(name = "username")
private String username;
#Column(name = "password")
private String password;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "zmember", joinColumns = {#JoinColumn(name = "username")}, inverseJoinColumns = {#JoinColumn(name = "id")})
private Set<Group> groups = new HashSet<Group>(0);
Member link table
#Entity
#Table(name = "member")
public class Member implements Serializable
{
#Id
#GeneratedValue
#Column(name = "id")
private int id;
#Id
#Column(name = "sgpid")
private int sgpid;
#Column(name = "username")
private String memberUsername;
Group
#Entity
#Table(name = "group")
public class Group
{
#Id
#GeneratedValue
#Column(name = "id")
private int id;
What is happening is there is no association to the link Member table so ideally should User have a set of member objects rather than a set of groups?
Thanks - this was quite hard to explain so sorry if it is hard to understand
This is a typical case for the #ManyToMany annotation. See for example:
https://dzone.com/tutorials/java/hibernate/hibernate-example/hibernate-mapping-many-to-many-using-annotations-1.html
The relationship from User to Group is essentially ManyToMany. You could model this is using the #ManyToMany annotation however one drawback with this approach is you cannot save additional information about the group in the join table such as 'date_joined'.
See: https://en.wikibooks.org/wiki/Java_Persistence/ManyToMany#ManyToMany
Using this approach you would not need the Join entity Member and the relationship on User would look like:
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "zmember", joinColumns = {#JoinColumn(name = "member_id", referencedColumnName = "id")}, inverseJoinColumns = {#JoinColumn(name = "group_id", referencedColumnName = "id")})
private Set<Group> groups = new HashSet<Group>(0);
The alternative to using #ManyToMany is to use a Join entity Member(ship) as you have done. This would allow you to save additional data about the relationship (by defining additional field mappings in the Join entity).
In this case the mappings would look like:
User:
public class User{
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<Membership> memberships = new HashSet<Membership>(0);
//if required, you can 'hide' the join entity from client code by
//encapsulating add remove operations etc.
public void addToGroup(Group group){
Membership membershup = new Membership();
membership.setUser(this);
membership.setGroup(group);
memberships.add(membership);
)
public Set<Groupp> getGroups(){
//iterate memberships and build collection of groups
}
}
Membership:
public class Membership{
#Id
#GeneratedValue
#Column(name = "id")
private int id;
#ManyToOne
#JoinColumn(name = "user_id")
private Member member;
#ManyToOne
#JoinColumn(name = "group_id")
private Group group;
}
Group:
#Entity
#Table(name = "group")
public class Group
{
#Id
#GeneratedValue
#Column(name = "id")
private int id;
#OneToMany(mappedBy = "group", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<Membership> memberships = new HashSet<Membership>(0);
}
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();
}