Using seperate OneToManys to create a ManyToMany Join table - mysql

I've got 2 models User and Exercise. Now any User can have any Exercise. It's a ManyToMany situation. I modeled it with #ManyToMany, but you can't have a duplicate entry in a ManyToMany. A User is likely to do multiple sets of one exercise so I duplicate entries are required. To get round this I created the join table separately called UserExerciseJoin. User and Exercise had ManyToOne relationships with the UserExerciseJoin model. Though this solved the multiple keys issue I now can't delete from the new table. I get an OptimisticLockException from some of the models associated to the Exercise.
My question is: Am I on the right path with the seperate table or is there something I can do to a standard #ManyToMany to make it accept duplicate entries?

If I understand it right in your model, then yes, it is probably not the case for #ManyToMany. It seems to me that you can be better off with a meaningful entity like UserExerciseOccurrence that reference both a User and an Exercise and means a concrete exercise session.
You can also benefit from this approach if you need to save more info about a particular exercise session (like duration, etc).
#Entity
class UserExerciseOccurrence {
#ManyToOne
User user;
#ManyToOne
Exercise exercise;
}
#Entity
class User {
#OneToMany(mappedBy="user", cascade=DELETE)
Set<UserExerciseOccurrence> exerciseOccurrences;
}
#Entity
class Exercise {
#OneToMany(mappedBy="exercise", cascade=DELETE)
Set<UserExerciseOccurrence> exerciseOccurrences;
}

You are on the right path. You should have #OneToMany relation from User class and from Excercise class to this new entity. And in UserExerciseJoin you should have #ManyToOne relations.
So this code should look like this:
#Entity
User {
#OneToMany(mappedBy="user")
private List<UserExercise> userExercises;
....
}
#Entity
Excercise {
#OneToMany(mappedBy="excercise")
private List<UserExercise> userExercises;
....
}
#Entity
UserExercise
{
#ManyToOne
private User user;
#ManyToOne
private Excercise excercise;
...
}
You had an error when deleting this new entity. You had in on some entity related to excercise. It seems that this is because of cascades. You probably set cascades on fields of UserExerciseJoin class. If it was CascadeType.DELETE or CascadeType.ALL cascade then it caused deletion of related entities. So you shouldn't set cascades in UserExercise class. Then deleting of such entity will not cause a problem.

Related

JPA: How to represent JoinTable and composite key of a JoinTable?

Let's say I have a webapp where users can follow other users.
In my database, I have a User table, and a Following table.
The Following table just has two values: a followingUserId and a followedUserId.
In Java, I have a User class. Most tutorials I see involve one object containing a set of objects it's related to. So many tutorials can describe how to have Users have a set of users following that user, and a set of users followed by a user. But that would require a lot of memory.
I'm thinking of an alternate structure where a User object has no info about following. Instead, there is a Following object that looks like this
#Entity
#Table(name = "Following")
public class Following {
RegisteredUser follower;
RegisteredUser followed;
}
and corresponds to the join table. When I want to get all the followers of a user, I can do a query for all Following objects with that user as the follower.
My issues are:
The Followers Table has a composite key of each of the two userids. How can I use annotations to represent that composite key? The #Id annotation denotes a single variable as the key
How can I do such a query?
If it's relevant, I am using MySQL as the db
If using JPA > 2.0, you can mark relationships as your ID:
#Entity
#Table(name = "Following")
#IdClass(FollowingId.class)
public class Following {
#Id
RegisteredUser follower;
#Id
RegisteredUser followed;
}
public class FollowingId implements Serializable {
private int follower;
private int followed;
}
the types within the followingId class must match the type of the RegisteredUser Id. There are plenty of more complex examples if you search on JPA derived ID.
For queries, it depends on what you are looking for. A collection of followers for a particular user could be obtained using JPQL:
"Select f.follower from Following f where f.followed = :user"

How do I auto-generate IDs for an #ElementCollection when it is a java.util.Map?

I’m using MySQL 5.5.37, JPA 2.0, and Hibernate 4.1.0.Final (I’m willing to upgrade if it solves my problem). I have the following entity …
#Entity
#Table(name = "url")
public class Url implements Serializable
{
…
#ElementCollection(fetch=FetchType.EAGER)
#MapKeyColumn(name="property_name")
#Column(name="property_value")
#CollectionTable(name="url_property", joinColumns=#JoinColumn(name="url_id"))
private Map<String,String> properties;
The “url_property” table has an ID (primary key) column, and perhaps for this reason, when I create a new Url entity with multiple properties, I feet the exception
[ERROR]: org.hibernate.engine.jdbc.spi.SqlExceptionHelper - Duplicate entry '' for key 'PRIMARY'
upon saving. Does anyone know what I have to do to auto-generate IDs for my url_property table? I would prefer not to write a trigger, but rather do something JPA, or at least, Hibernate sanctioned.
Edit: Per the first suggestion in the answer, I tried
#ElementCollection(fetch=FetchType.EAGER)
#Column(name="property_value")
#CollectionTable(name="url_property", joinColumns=#JoinColumn(name="url_id"))
private Set<UrlProperty> properties;
but it resulted in the exception, "org.hibernate.MappingException: Foreign key (FK24E4A95BB0648B:url_property [properties_id])) must have same number of columns as the referenced primary key (url_property [url_id,properties_id])".
My UrlProperty entity is
#Entity
#Table(name = "url_property")
public class UrlProperty
{
#Id
#GeneratedValue(generator = "uuid-strategy")
private String id;
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name="URL_ID")
private SubdomainUrl url;
#Column(name="PROPERTY_NAME")
private String propertyName;
#Column(name="PROPERTY_VALUE")
private String propertyValue;
You have only told JPA about 3 fields in the table ("property_name","property_value" and "url_id"), so it has no way of knowing about the 4th field used as the pk. Since it is not an entity, it doesn't have an Identity that is maintained. Options are:
1) Map the "url_property" table to a Property entity, which would have an ID, value and reference to the Url. The Url would then have a 1:M reference to the Property class, and can still be keyed on the name. http://wiki.eclipse.org/EclipseLink/Examples/JPA/2.0/MapKeyColumns has an example
2) Change your table to remove the ID field, and instead use "property_name","property_value" and "url_id" as the primary key.
3) Set a trigger to populate the ID. Doesn't seem useful though since the application is never aware of the field anyway.

JPA one-to-many delete and set foregn key to null

I use Spring Data, JPA, Hibernate and MySQL. I have one to many relation between Event and Category. Obviously one event can have only one category and one category can be assigned many Events. The problem appears when I try to remove Category, if any event hold a foreign key of this category then I get an error. I would like to set the foreign key in the Event table to null when a Category is deleted. At the moment I update all Events by setting the foreign key explicitly in the code by updating it to null before deletion of Category. Is there any way of doing it in use of annotations?
This is my Category:
#Entity
#Table(name = "category")
public class Category implements Serializable{
#OneToMany(mappedBy="category", targetEntity=Event.class, fetch=FetchType.LAZY, cascade=
{CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
public Set<Event> getEvents_category() {
return events_category;
}
}
And the Event class:
#Entity
#Table(name = "event")
public class Event implements Serializable{
#ManyToOne(cascade={CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST,
CascadeType.REFRESH})
#JoinColumn(name="events_dancestyle")
public DanceStyle getDanceStyle() {
return danceStyle;
}
}
I've seen that the topic was discussed many times but I haven't seen any solution to that.
Unfortunately it is not currently possible to do this using JPA/Hibernate annotations. See these related questions:
Have JPA/Hibernate to replicate the "ON DELETE SET NULL" functionality
On delete set null in hibernate in #OneToMany
1- the best way is to write cascade = CascadeType.DETACH
2- but you should write above Class Event
#SQLDelete(sql = "UPDATE event SET deleted=true WHERE id=?")
3- inside the Event Class
#Column(columnDefinition = "boolean default false")
private boolean deleted;
4- Then when you delete category
if CascadeType.ALl the "deleted" attribute of Event entity will affected by 1 (true).
if CascadeType.DETACH the "deleted" attribute of Event entity will not affected and still 0 (false).

ejb3 toplink jpa 1.0 querying and sequencing

I have 2 questions:
suppose we have one entity named class and another called student. each class has onetomany students.
public class Clas implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE)
private int id;
#OneToMany(cascade=CascadeType.ALL)
Collection<Student> students;
public clas(){
super();
}
..... getters and setters
}
q1: i get the exception there are no fields to be mapped, when adding any other column like String name, it works, but i don't need that field what can i do ?
q2: the ids is autogenerated, and i want to query all students in class c1, but i don't has the id of this class, how to do such query ?
iam working with mysql server glassfish v2.1 toplink jpa 1.0
Thanks
The student class must have a property named 'classID' (or whatever) that refers to the
Clas's id property. That should be annotated like #ManyToOne.
If that's done already by IDE, then check id generation strategy. For example, if you are using mysql, the primary key is auto_increment, then set th id's strategy to
GenerationType.AUTO and recompile. Tell me if any other errors shows up. :) .
ok. I think I understood you question. You may use NamedQueries written in Query Languages dependent on your library (in your case toplink) like EJB QL or HBQL. You can create Session Beans for querying.
public class ClassSessionBean {
#PersistenceContext(unitName="your PU name in persistence . xml")
private Entitymanager em;
publicClas selectByID(int id) throws NoResultException {
Query q = em.createQuery("select class from Class class where class.id=?");
q.setParameter(1, id);
Clas clas = q.getResultList();
return clas;
}
}
Note that the above code may contain syntax errors because I have not checked it anywhere.
Hope you find some help from this :) .

How to do a many-to-many relationship in spring Roo, with attributes within de relationship?

i have been researching on this topic and havent found any answers yet. Im using spring roo and i would like to know if theres a way I can establish a many-to-many relationship with attributes within this relationship. For example i have two tables Employee and MedicalEquipment, Employees can reserve many equipments, and equipment could be reserved by many employee, however i want to store the date this reserve took place.
If some one can help i would appreciate it. Thanks in advance!
In order to achieve a many-to-many relationship with attributes, you need a join table which contains the additional columns. That is, besides Employee and MedicalEquipment, you will need a third EmployeeMedicalEquipment.
Regarding JPA you have two options:
Mapping the join table to an intermediate entity
Mapping the join table to a collection of components
The former is more complicated, but it allowed you to have bidirectional navigation (because it's an entity and hence, can have shared references), the latter is simpler in both building it and using it, but you can't use it to navigate between entities (however, you can write a query to retrieve the objects you need)
Roo is a code generator, but it lacks of all the options that JPA offers, so you have to edit the Java classes, and the test classes. The building of the view has some limitations too, so you will need to edit it as well.
In my case, I needed to create an intermediary entity because the table belongs to a legacy database that already existed.
I did something like this:
entity --class ~.domain.Employee --table T_Employee [etc...]
field [etc...]
entity --class ~.domain.MedicalEquipment --table T_MedicalEquipment [etc...]
field [etc...]
entity --class ~.domain.EmployeeMedicalEquipment --table T_Employee_MedicalEquipment --identifierType ~.domain.EmployeeMedicalEquipmentId
//to store the date this reserve took place.
field date --fieldName reserveDate --column C_reserveDate [etc...]
//Bidirectional: you´ll need #JoinColumn insertable = false and updatable = false
field reference --fieldName employee --type ~.domain.Employee --cardinality MANY_TO_ONE
//Bidirectional: you'll need #JoinColumn insertable = false and updatable = false
field reference --fieldName medicalEquipment --type ~.MedicalEquipment --cardinality MANY_TO_ONE
//Join table's composite primary key
field string --fieldName employeeId --column employee_ID --class ~.domain.EmployeeMedicalEquipmentId [etc...]
field string --fieldName medicalEquipmentId --column medicalEquipment_ID --class ~.domain.EmployeeMedicalEquipmentId [etc...]
//Now, it's time to complete the relationship:
focus --class ~.domain.Employee
field set --type ~.domain.EmployeeMedicalEquipment --fieldName medicalEquipments --cardinality ONE_TO_MANY --mappedBy employee
focus --class ~.domain.MedicalEquipment
field set --type ~.domain.EmployeeMedicalEquipment --fieldName employees --cardinality ONE_TO_MANY --mappedBy medicalEquipment
Furthermore, you need to guarantees referential integrity by managing collections on either side of the association using the constructor. So you need to edit the class in such way:
#RooEntity(...
public class EmployeeMedicalEquipment {
#ManyToOne
#JoinColumn(name = "employeeId", referencedColumnName = "employeeId", insertable = false, updatable = false)
private Employee employee;
#ManyToOne
#JoinColumn(name="medicalEquipmentId", referencedColumnName="medicalEquipmentId", insertable=false, updatable=false)
private MedicalEquipment medicalEquipment;
/**
* No-arg constructor for JavaBean tools
*/
public EmployeeMedicalEquipment() {
}
/**
* Full constructor, the Employee and MedicalEquipment instances have to have an identifier value, they have to be in detached or persistent state.
* This constructor takes care of the bidirectional relationship by adding the new instance to the collections on either side of the
* many-to-many association (added to the collections)
*/
public EmployeeMedicalEquipment(Employee employee, MedicalEquipment medicalEquipment, Date reserveDate) {
this.setReserveDate (reserveDate);
this.employee = employee;
this.medicalEquipment = medicalEquipment;
this.setId(new EmployeeMedicalEquipmentId(employee.getId(), medicalEquipment.getId());
// If Employee or MedicalEquipment Guarantee referential integrity
employee.getMedicalEquipments().add(this);
medicalEquipment.getEmployees().add(this);
}
...
}
I tried to give you an example of a Roo configuration.
You can find a better explanation of the JPA stuff in the book from Manning "Java Persistence with Hibernate", chapter 7.2.3.
Note: if you use roo 1.2.1, the count query will generate SQL with "count(id1, id2)", which is not supported by all databases, including HSQLDB. You can customize it like this:
...
-#RooJpaActiveRecord(identifierType = EmployeeMedicalEquipmentId.class, table = "T_Employee_MedicalEquipment")
+#RooJpaActiveRecord(identifierType = EmployeeMedicalEquipmentId.class, table = "T_Employee_MedicalEquipment", countMethod="")
...
public class EmployeeMedicalEquipment {
...
// count method initially copied from ActiveRecord aspect
public static long countEmployeeMedicalEquipments() {
- return entityManager().createQuery("SELECT COUNT(o) FROM EmployeeMedicalEquipment o", Long.class).getSingleResult();
+ return entityManager().createQuery("SELECT COUNT(o.employee) FROM EmployeeMedicalEquipment o", Long.class).getSingleResult();
}
}
Why can't you have a separate entity to denote your relationship?
Just introduce a new entity called a MedicalEquipmentReservation which would contain all the attributes of the reservation along with the relationships between the Employee and the Medical Equipment entities.
See the following example.
class MedicalEquipmentReservation{
//attributes
Date reservationStartDate;
Date reservationEndDate;
//relationships
Employee employee;
Set<Equipment> equipments; //assuming more than one equipment can be borrowed at one reservation
}
Cheers and all the best with Spring Roo!