Best way to model one to many relationship in OOP - oop

I'm working in a project which involves a one to many relation in the database.
A simple example of this would be a teacher that teaches many courses but a course can be taught by just one teacher.
My question is what would be the best way to model this?
The first object is Teacher:
class Teacher{
public int id;
public String name;
public String lastName;
...
}
The thing is how would the course class look like?
Option 1:
class Course{
public int idCourse;
public String courseDescription;
**public int teacherId;**
...
}
Option 2:
class Course{
public int idCourse;
public String courseDescription;
**public Teacher teacher;**
...
}

Option 2 (having a reference to the teacher object) seems to be more functional from an OOP perspective.
In a usual flow, you would create a course object like :
Teacher t = new Teacher('first_name', 'last_name');
Course c = new Course('Math course');
c.setTeacher(t);
Your option 1 highlights what the relational database schema behind this might look like. Here's a good read on that :
https://www.lifewire.com/one-to-many-relationships-1019756#:~:text=A%20teacher%20can%20teach%20multiple,one%20teacher%20to%20multiple%20courses.
Once the code above is committed, it could fire two insert queries underlying to add the new teacher in teachers table and add the new course (along with the teacher ID), in the course table.

Related

Friend Relationships with JPA

I'm having a hard time creating the optimal JPA representation of a relationship. Seems like there is a bunch of ways to do it and I'm not sure which is most optimal. Without JPA I might represent the structure as a PERSON table and a FRIEND table. Let's say the PERSON table just has an ID and NAME. The FRIEND table has an OWNER_ID, a PERSON_ID, and a settable boolean IS_ACTIVE field. OWNER_ID and PERSON_ID both refer to the PERSON table. A person can have many friends so the unique primary key would be on OWNER_ID and PERSON_ID. In my code I would like the PersonEntity to control the relationship. Operations in Java might look like this:
person1.getFriends().add( new Friend( person2, isActive ) );
Friend friend = person1.findFriend( person2ID );
Something like that. Note that I would like JPA to implicitly assign the OWNER of the friend relationship. In the above code I do not pass it in the Friend constructor, I only pass the PERSON part of the composite key.
Here was my first guess at this:
#Entity
public class Person {
#Id
#GeneratedValue
private Long id;
#OneToMany( mappedBy="key.owner", cascade=CascadeType.ALL, orphanRemoval = true)
private Set<Friend> friends = new HashSet<>();
// getter and setter for Friends
...
}
#Entity
public class Friend {
#EmbeddedId
private Key key = new Key();
private boolean isActive;
...
#Embeddable
public static class Key implements Serializable {
#ManyToOne
private Person owner;
#OneToOne
private Person person;
...
}
}
But when I use this code I get stack overflow errors. I'm assuming it is confused by the OneToOne relationship.
What is the best way to model this sort of relationship in JPA 2.0? I suppose the simplest thing would be to introduce a generated long key in Friend. But that seems like it will make the query more complex for "find friends for john" since I will be introducing a third mapping table, basically mapping the relationship twice in SQL. Another approach is to make it a unidirectional OneToMany relationship and not even specify OWNER explicitly in Friend. That would introduce another table for the mapping and make queries a little convoluted. Using ElementCollection instead of OneToMany seems straight forward but then my understanding is that I could not manage Friend as an entity?
Let me know if you guys need anymore detail or requirements. BTW, I tried putting a MapsId in Friend for the Owner part of the PKEY but that gave me runtime errors.
Any help is appreciated.
NOTE: Even if I add a generated key to Friend I would like to enforce the uniqueness on the combination of {owner,person} in the Friend entity.
I would indeed add an auto-generated ID to the Friend entity. It would make things much simpler (no composite key containing associations). I don't see how adding such an ID (and thus such an additional column in the Friend table) would make any query more complex, or introduce a new table. It won't. You'll simply have an additional column, being the primary key of the friend table.
Once you have that, you'll need to fix your mapping:
the association between Friend and Person is not a OneToOne, but a ManyToOne, since several persons can be the friends of another one.
mappedBy is supposed to hold the name of the field, in the other entity, which represents the other side of the association. In your case, it's thus "key.owner". If you stop using a composite key, it will simply be "owner".
And finally, in a bidirectional OneToMany association, the owner is always the Many side (i.e. Friend, in this case). So you must initialize the friend.owner field in order to persist the association. But if you encapsulate the list of friends rather than letting it accessible from the oustide, it will be simple. Instead of doing
person1.getFriends().add( new Friend( person2, isActive ) );
simply do
person1.addFriend( new Friend( person2, isActive ) );
and make sure that the addFriend() method contains the following instruction:
friendToAdd.setOwner(this);
Alright, so if you want a solution to this problem that creates the sort of table one would want to in pure SQL - that is no artificial surrogate key and application data as a compound key - you can definitely do it. However, using JPA makes this a little bit messier than you might want it to be.
Thanks to #JB Niznet for his great input, you can definitely also do this by introducing a surrogate key as he suggests. The solution below just removes that need and as far as I can tell has no drawbacks.
#Entity
public class Person {
#Id
#GeneratedValue
private Long id;
#OneToMany( mappedBy="owner", cascade=CascadeType.ALL, orphanRemoval = true)
private Set<Friend> friends = new HashSet<>();
// getter and setter for Friends
...
}
#Entity
public class Friend {
#EmbeddedId
private Key key = new Key();
#ManyToOne
#Maps("ownerId")
private Person owner;
#ManyToOne
#MapsId("personId")
private Person person;
private boolean isActive;
...
#Embeddable
public static class Key implements Serializable {
private Long ownerId;
private Long personId;
...
}
}
You have to think about the Design: Entities will be classes and have tables. Relations will ONLY have tables, since relations are just connecting 2 Entities! (except a relation has attributes, like "friend-rating" in this case etc..)
While Person is clearly a Entity, friend is a relation. More than that, friend is a bidirectional relation. (A is friend of B => B is friend of A)
So, you simple need to extend your Person Object, by a List of Persons - called friends:
#Entity
public class Person {
#Id
#GeneratedValue
private Long id;
#OneToMany
#JoinTable(name="friends")
#JoinColumn(name="person_A_id", referencedColumnName="id"), #JoinColumn(name="person_B_id", referencedColumnName="id"))
private Set<Person> friends = new HashSet<>();
}
This is untestet, but should give you a table like this:
person_a_id | person_b_id
1 2
1 6
2 7
then you simple can work with your entity like
Person A = new Person();
Person B = new Person();
A.friends.add(B);
However, keep in Mind that JPA will not be able to thread this bidirectional: So if you add B to A'S friends, you wont find A in the List of Friends of B. You have to take care for that!

Fluent NHibernate automapping: One-to-many entities, many-to-many backend?

My goal is to use NHibernate schema generation along with Fluent NHibernate's automapper to generate my database. I'm having trouble with what I'll call "unidirectional many-to-many relationships."
Many of my entities have localized resources. A single class might look like this:
public class Something {
public virtual int Id {get; private set;}
public virtual Resource Title {get;set;}
public virtual Resource Description {get;set;}
public virtual IList<Resource> Bullets {get;set;}
}
The Resource class doesn't have any references back; these are entirely unidirectional.
public class Resource {
public virtual int Id {get; private set;}
public virtual IList<LocalizedResource> LocalizedResources {get;set;}
// etc.
}
public class LocalizedResource { //
public virtual int Id {get; private set; }
public virtual string CultureCode {get;set;}
public virtual string Value {get;set;}
public virtual Resource Resource {get;set;}
}
Without the IList<Resource>, everything is generated as I'd want -- Resource ID's are in the Title and Description fields. When I add in the IList though, NHibernate adds the field something_id to the Resource table. I understand why it does this, but in this situation it's not a sustainable approach.
What I want is to create a junction table for the bullets. Something like:
CREATE TABLE SomethingBullet (
Id int NOT NULL PRIMARY KEY IDENTITY(1,1),
Something_Id int NOT NULL,
Resource_Id int NOT NULL
)
This way when I add the other twenty-odd entities into the database I won't end up with a ridiculously wide and sparse Resource table.
How do I instruct the Automapper to treat all IList<Resource> properties this way?
Every many-to-many is in fact composed with one-to-many's in object model. If your relationship doesn't need to be bidirectional, just don't map the second side. The mapping on your mapped side is not affected at all:
HasManyToMany(x => x.Bullets).AsSet();
In this case, NHibernate already knows that it needs to generate the intermediate table.
See also this article for many-to-many tips.
:)
The only way I found to make this work with automapping is by constructing your own custom automapping step and replacing the "native" HasManyToManyStep. It's either that or an override, I'm afraid.
I lifted mine off of Samer Abu Rabie, posted here.
The good news is that Samer's code, so far, seems to work flawlessly with my conventions and whatnots, so, once it was in place, it was completely transparent to everything else in my code.
The bad news is that it costs you the ability to have unidirectional one-to-many relationships, as Samer's code assumes that all x-to-many unidirectional relationships are many-to-many. Depending on your model, this may or may not be a good thing.
Presumably, you could code up a different implementation of ShouldMap that would distinguish between what you want to be many-to-many and what you want to be one-to-many, and everything would then work again. Do note that that would require having two custom steps to replace the native HasManyToManyStep, although, again, Samer's code is a good starting point.
Let us know how it goes. :)
Cheers,
J.

nhibernate collection query with private backing field

I have a mant-to-many relationship modeled in the database (with a bridge table) between Student and Professor (_students_selected) , in my entites i have modeled it as a one-to-many relationship i.e. a Professor has one Student.
HasManyToMany<Student>(Reveal.Member<Professor>("_students"))
.Table("_students_selected").ChildKeyColumn("student_key").ParentKeyColumn("professor_key");
public class Professor
{
private IList<Students> _students;
public virtual Student Student
{
get { return _students.FirstOrDefault(); }
}
}
The above works when getting the data however when querying over the Professors i am unable to add a where condition on the students because the actual data is mapped to the private backing field _students. How do i query this? code below does not work.
_unitOfWork.Session.QueryOver<Professor>().Where(i => i.Student.Id == 24).List();
NHibernate can't translate your C# code inside the property to SQL, it can only work with mapped properties. Either use the collection in the statement (which needs to be public/internal then of course) or filter the results in memory (but be careful with select n + 1 problems then).

Composition over Inheritance - where do extra properties go?

Take this following code from an example HR system. The user has the ability to log an absence and can be of various types including holiday and sickness. This would be a domain model over an ORM such as NHibernate.
public class Absence
{
public long Id {get;set;}
public Employee Employee {get;set;}
public DateTime StartDate {get;set;}
public DateTime EndDate {get;set;}
public virtual void DoSomething()
{ ... }
}
public class Holiday : Absence
{
public string Location {get;set;}
public override void DoSomething()
{ ... }
}
public class Sickness : Absence
{
public bool DoctorsNoteProvided {get;set;}
public override void DoSomething()
{ ... }
}
This is an example - please don't question why location would be required, assume it is a specification.
The user wants to change the type - he thought the employee was off sick but then remembered it was a holiday. Again, you may think this is a bad design but treat it like a requirement - this represents a problem that has come up many times for me.
The problem is that you cannot change the type of an object from Sickness to Absence. Generally, the advice would be to Favour Composition Over Inheritance (Gang of Four) and do this:
public class Absence
{
public long Id {get;set;}
public Employee Employee {get;set;}
public DateTime StartDate {get;set;}
public DateTime EndDate {get;set;}
public AbsenceType Type {get;set;}
public void DoSomething()
{
Type.DoSomething();
}
}
But when I do this, when do the properties specific to Holiday and Sickness go (Location and DoctorsNoteProvided respectively)?
Why do you need to change the type of an object?
You will have some kind of collection of Absences, just replace the item in question.
Conceivably rather than replacing you even keep the original request and mark it as superceded, that might be important for audit trail purposes.
It's not the right place for Composition over Inheritance. Here the inheritance is appropriate. And if you need to change the type of absence just create a new one and delete old.
Hmmm, without knowing more about your requirements, I would say the right design is not to change an Absence object to a Sickness object (or vice versa) but to just delete the one you don't want and create a new one of the type you do. Somewhere you must be maintaining a collection of absences, right?
You are correct that classes don't change.
I would model this by having a type hierarchy for an AbsenceType, or AbsenseReason:
abstract class AbsenseReason {
}
class HolidayAbsenseReason : AbsenseReason {
public string Name { get; }
}
I like this model because now AbsenseReason is a value object and is independent of an employee Absence, which is an entity object. This, as you stated, solves the issue with changing the absence reason. Generally speaking, I would favor this over deleting a record, because there may be many associations to consider as well.
Things to consider:
NHibernate does not support inheritance mappings on components so you will have to provide a custom implementation of IUserType.
Consider storing all the data for the different absence reason sub types together with the record for the employee absence entity. Possibly as XML so that you can have collections, etc.
So try to move all type specific functionality to AbsenceType derivatives. If they require something from parent class Absence, you could pass them its reference. Though I would try to avoid that.
If you manipulated Absence object via base class interface, nothing changes, you can keep your old code. Now, if you manipulated specific derivatives, then you will have to grab AbsenceType object from specific Absence and do all the same things on them - still not much to change. If you had holiday.DoSomething(), now you have holiday.Type.DoSomething().

NHibernate convert subclass to parent class

Supposing the following entities :
public class AppUser
{
public virtual int Id { get; set; }
public virtual string Login { get; set; }
}
// Mapped as joined-subclass
public class Person : AppUser
{
public virtual int Age { get; set; }
}
If I create 1 AppUser, and save it like this
var user = new AppUser() { Login = "test" };
session.Save( user ); // let's say Id = 1
How can I cast/convert/"promote" it to a Person, keeping the same ID ?
Now, i'm stuck with a row in my AppUser table, with Id = N. How can I populate the Person table with the same Id ? I can't delete the AppUser and recreate it as a Person, as AppUser may be referenced by foreign keys.
I could issue a "manual" SQL INSERT, but it's kind of ugly...
This is definitively a NHibernate question. I understand that from an OOP point of view, this makes little sense, hence the absence of other tags than nhibernate.
I don't believe nHibernate is going to be able to solve this problem for you. nHibernate is dealing with your data as an object and, especially with joined-subclass I don't believe there is anything built in that allows you to change the subclass type on the fly, or at least change the type and retain the original ID.
I think your best bet is to write a stored procedure that, given an ID and a NEW type, removes all entries from subclass tables and adds a new entry to the correct subclass table.
Once that proc runs, then reload the object in nHibernate (and make sure you have thrown away any cached data relating to it), it should now be of the correct type you want to work with, set its NEW properties and save it.
That way you have a relatively generic stored proc that just changes your subclass types, but you dont need to add all the crazy logic to handle various properties on your subclasses.
This has been discussed on SO before and I am quoting Jon Skeet for posterity:
No. A reference to a derived class
must actually refer to an instance of
the derived class (or null). Otherwise
how would you expect it to behave?
For example:
object o = new object();
string s = (string) o;
int i = s.Length; // What can this sensibly do?
If you want to be able to convert an
instance of the base type to the
derived type, I suggest you write a
method to create an appropriate
derived type instance. Or look at your
inheritance tree again and try to
redesign so that you don't need to do
this in the first place.
In Skeet's example, string's are objects and objects are not strings. So the "upcasting" would not work.