My app has 2 java pojo classes linked via ManyToMany relationship User & Season:
#Entity
#Table(name = "user")
public class User implements Serializable {
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(name = "user_season", joinColumns = { #JoinColumn(name = "user_id") }, inverseJoinColumns = { #JoinColumn(name = "season_id") })
private Set<Season> followingSeason;
Set<Season> getSeasonsWhichTheUserFollows(){
return this.followingSeason;
}
}
Season class
#Entity
#Table(name = "season")
public class Season implements Serializable{
#ManyToMany(mappedBy = "followingSeason", fetch = FetchType.EAGER)
private Set<User> user;
}
When a user unfollows a season unfollowedSeason object I remove it from the set of season which the user follows.
Set<Season> seasonSet = user.getSeasonsWhichTheUserFollows();
seasonSet.remove(unfollowedSeason);
user.setFollowingSeason(seasonSet );
this.userService.update(user);
well this removes the entry from the user_season bridge table, everything is fine. But at the same time I also want to update some fields of the Season entity in the db for an instance decrementing the count of users following by 1. Is there a way I can do that within the same call? Or do I have to run a separate query to update the season entity?
Not sure if i got that right, but why can't you just put something in there like unfollowedSeason.setCount(unfollowedSeason.getCount() +1 ) and then just update the season?
EDIT AFTER DISCUSSION IN COMMENTS:
What you want to do is not possible because
you can't do a update and a remove in the same SQL Statement(as over9k stated)
Related
I have a Subscription class and Payment class. When I do the following, it doesn't create a record in join table. Should I use intermediate class or is it possible to create such record without it? subscriptionRepository is a CrudRepository from Spring-Data.
#Transactional
public Subscription activate(#Valid Subscription subscription, #Valid Payment payment) {
Set<Payment> payments = subscription.getPayments();
if (payments == null)
payments = new HashSet<>();
payments.add(payment);
return subscriptionRepository.save(subscription);
}
Classes:
Subscription:
#Entity
public class Subscription {
...
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(
joinColumns = {#JoinColumn(name = "subscription_id", referencedColumnName = "id")},
inverseJoinColumns = {#JoinColumn(name = "payment_id", referencedColumnName = "id", unique = true)}
)
#Getter #Setter
private Set<Payment> payments;
}
Payment:
#Entity
public class Payment {
#Column
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#JsonIgnore
private Integer id;
#Column(nullable = false)
private PaymentType paymentType;
#Past
#Column(nullable = false)
private Date date;
public enum PaymentType {
MONEY,
PROMO_CODE,
TRIAL
}
}
you forgot to inject the payments in the subcription , your repository and pojo seem just fine
if (payments == null) {
payments = new HashSet<>();
subscription.setPayments(payments);
}
First of all, you need to mark your method with #Transactional annotation, cause the Spring Data save method does not execute explicit save action, it just selects a database row identifier and sets it to your entity.
1) Mark your method as #Transactional (best solution)
2) Inject EntityManager and create a transaction manually.
P.S.: JPA Persistence with Hibernate advises to initialize your collections in your model class (No lazy initialization). It reduces a lot of boilerplate checks and sometimes the realization shows Hibernate which Hibernate built-in collection to use (bags etc)
I'm working on a java spring mvc application with hibernate. I have two Entities Acl and AclGroupand These two entities have Many to Many relationship with a join table. But, when I save an AclGroup object, hibernate doesn't insert any record in join table and just inserts into AclGroup table. Here is structure of my classes:
Acl.java:
public class Acl implements Serializable{
...
#JoinTable(name = "acl_group_acl", joinColumns = {
#JoinColumn(name = "acl_id", referencedColumnName = "id")}, inverseJoinColumns = {
#JoinColumn(name = "acl_group_id", referencedColumnName = "id")})
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Collection<AclGroup> aclGroupCollection;
public Collection<AclGroup> getAclGroupCollection() {
return aclGroupCollection;
}
public void setAclGroupCollection(Collection<AclGroup> aclGroupCollection) {
this.aclGroupCollection = aclGroupCollection;
}
...
}
AclGroup.java:
public class AclGroup implements Serializable{
...
#ManyToMany(mappedBy = "aclGroupCollection",fetch = FetchType.LAZY)
private Collection<Acl> aclCollection;
public Collection<Acl> getAclCollection() {
return aclCollection;
}
public void setAclCollection(Collection<Acl> aclCollection) {
this.aclCollection = aclCollection;
}
}
Here is how I save my object:
AclGroup aclGroup = new AclGroup();
List<Acl> acls = new ArrayList<>();
/*
add some elements to acls
*/
aclGroup.setAclCollection(acls);
/*
hibernate config and opening a session
*/
session.save(aclGroup); //session.persist also did not work
Could anyone help me to solve this problem? Thank you for your attention.
The owner side of the association is Acl. AclGroup is the inverse side (since it has the mappedBy attribute). Hibernate only cares about the owner side.
So make sure to add the group to the acl when you add the acl to the group: that will work whatever the owner side is, and will make your graph coherent. Or, if you absolutely don't want to do that, put the mapping annotations on AclGroup, and make Acl the inverse side.
I have two entities, User and Event. Each event can have multiple users associated with it, so its a one to many between Event and User.
The way its being stored in the database, is that I have 3 tables, user, event, and event_user. event_user contains 3 fields, id, eventId, userId. So I can do a query like select userId from event_user where eventId = ? to get all the users which are associated with the event.
My question is, how can I map this relationship between the events and users in Hibernate, to get it to auto save/load the users associated with the event? I want to have the following field in the Event class:
Set<User> users = new HashSet<>();
and have hibernate auto load / save the users to this set.
How can I map this (using annotations)?
Use the #ManyToMany annotation.
class Event{
#ManyToMany(cascade=CascadeType.ALL)
#JoinTable(name = "EVENT_USER",
joinColumns = { #JoinColumn(name = "EVENT_ID") },
inverseJoinColumns = { #JoinColumn(name = "USER_ID") })
private Set<Users> users = new HashSet<Users>();
}
For more information on many to many associations in JPA check out this video tutorial at my blog.
Hibernate doc on the Bidirectional mapping using annotations should help
Basically you need to do something like this
#Entity
public class User implements Serializable {
#ManyToMany(
targetEntity=org.hibernate.test.metadata.manytomany.Event.class,
cascade={CascadeType.ALL}
)
#JoinTable(
name="USER_EVENT",
joinColumns=#JoinColumn(name="USER_ID"),
inverseJoinColumns=#JoinColumn(name="EVENT_ID")
)
public Set<Event> getEvents() {
return events;
}
...
}
#Entity
public class Event implements Serializable {
#ManyToMany(
cascade = {CascadeType.ALL},
mappedBy = "events",
targetEntity = User.class
)
public Set<User> getUsers() {
return users;
}
}
I am trying to get the following type of mapping to work
Table event has the following columns:
id (PK)
prodgroup
errandtype
table errandtype : errandtype
table prodgroup: prodgroup
I have corresponding JPA classes
#Entity
#Table(name="event")
public class MyEvent {
#Id
int id;
// what mapping should go here?
Prodgroup prodgroup;
// what mapping should go here?
ErrandType errandtype;
}
#Entity
public class Prodgroup {
#Id
private String prodgroup;
}
#Entity
public class ErrandType {
#Id
private String errandtype;
}
Ok so questions are marked as comments in the code but I'll try to be explicit anyway.
In the above example I want my Prodgroup and ErrandType fields in the MyEvent class to be set to corresponding Prodgroup and Errandtype instances
I have tried #OneToOne relationships with #joincolumns and with mappedby attribute, but I just can't get it working and I've lost all sense of logical approach. My grasp of JPA entity mapping is clearly weak.
So can anyone bring some clarity?
It should be:
#Entity
#Table(name="event")
public class MyEvent {
#Id
int id;
// what mapping should go here?
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "prodgroup_id", insertable = true, updatable = true)
Prodgroup prodgroup;
// what mapping should go here?
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "errandtype_id", insertable = true, updatable = true)
ErrandType errandtype;
}
#Entity
public class Prodgroup {
#Id
private String prodgroup;
}
#Entity
public class ErrandType {
#Id
private String errandtype;
}
FetchType Eager means the object will be always loaded (would be "Lazy" by default if not specified).
CascadeType.ALL means mearge/persist/remove will be also done to linked tables.
Sebastian
Your table columns event.prodgroup and event.errandtype are foreign keys to respective tables (prodgroup, errandtype). So you need #ManyToOne association (because many events may share one prodgroup or errantype).
I'm using these entities with JPA+Hibernate:
#Entity
public class Game {
#Id
#GeneratedValue
private long id;
#ManyToMany
#JoinTable(name="Game_admins")
private Set<User> admins = new HashSet<User>();
...
#Entity
public class User {
#Id
#GeneratedValue
private long id;
...
Thus far everything works well. Hibernate generates a join table called Game_admins with two colums, Game_id and admins_id.
However, if I add this to User:
#ManyToMany(mappedBy="admins")
private Set<Game> adminForGames = new HashSet<Game>();
The join table suddenly gets three columns, one of which is adminForGames_id. I don't need or want that, and I don't think it should be generated since I specify mappedBy. What am I doing wrong?
I believe you need to set the inverseJoin attriubtes
#ManyToMany
#JoinTable(name="Game_admins", joinColumns = {
#JoinColumn(name="admins_id")
},
inverseJoinColumns = {
#JoinColumn(name="Game_id")
}
private Set<User> admins = new HashSet<User>();