Inheritence Strategy when some entities do not have any specific columns or table - sql

In database we have a "parent" table with a discriminator column, and depending on this discriminator column some columns are stored in specific tables or some relationships (many/one-to-many) are allowed.
In our mapping we wanted to implement it like
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public class A {
#Id
#Column
private int id;
}
#Entity
public class B extends A {
#Column
private String specificField;
}
#Entity
public class C extends A {
#OneToMany
private List<OtherEntity> otherEntities;
}
Unfortunately Hibernate wants to join on table C, which does not exist since it would only contain the FK to A. Is there a way to keep this strategy but tell that no join is necessary?
Otherwise, what is the cleanest solution? Following this question I thought using the SINGLE_TABLE strategy with the #SecondaryTable annotation on child entities that require it but it seems heavier to configure (having to declare, for each column, the source table).

Related

Best way to model one to many relationship in 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.

Is there any way to specify table prefixes in ActiveJDBC models?

Is there any way to change the table name of the models, or specify a table prefix, so that the model named People, for example, would reference the table TP_PEOPLE?
There is a way, you can use the #Table annotation, such as:
#Table("TP_PEOPLE")
public class People extends Model {}
however, I would suggest to call your class Person, since the instance of this class represent a single row from your table:
#Table("TP_PEOPLE")
public class Person extends Model {}
so that your code will look:
List<Person> people = Person.where("ssn = ?", ssn);

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 different Conventions for different base types

At this moment we are keeping all Entities and Mappings into same assembly. Our entities derived from a basic class Entity which is an EntityWithTypedId
Also we are having a table name Convention telling to pluralize the table names.
Now I want to create other two base types e.q. AggregateRootEntity and AggregateEntity, both derive from Entity.
And I would like to create two set of conventions for both base entities:
Let's say:
For for all entities derived from AggregateRootEntity tables should be prefixed with "ag_" and Id is incremental generated, but for all entities derived from AggregateEntity tables should be prefixed with "a_" and Ids should be assigned.
Is it possible to Set Conventions based on some conditions?
You can do it with multiple conventions, each checking for a specific type in their Accept methods
something like:
public class LegacyEntityTableConvention : IClassConvention, IClassConventionAcceptance
{
public void Accept(IAcceptanceCriteria<IClassInspector> criteria)
{
criteria.Expect(x => x.EntityType.IsAny(typeof(OldClass), typeof(AnotherOldClass)));
}
public void Apply(IClassInstance instance)
{
instance.Table("tbl_" + instance.EntityType.Name);
}
}
Just a block of code out of the FNH Wiki
http://wiki.fluentnhibernate.org/Acceptance_criteria

Fluent NHibernate / NHibernate Inheritance Question

Possibly a dumb question but I have a number of entities all inheriting from a base entity. The base entity does not have a table in the database. Each entity has its own table and the table definition is exactly the same. Extremely simplified example of the code is below.
public abstract class BaseEntity
{
public virtual string someProperty {get; set;}
}
public class Entity1 : BaseEntity{}
public class Entity2 : BaseEntity{}
public class CompletelyDifferentEntity
{
public virtual IList<BaseEntity> {get; set;}
}
I created the mappings for the entities. In my other domain classes if I reference the concrete classes everything works fine but if I change my other classes to reference BaseEntity instead I get a mapping Exception because the BaseEntity is not mapped. Is this something where I should use a subclass discriminator? I guess I'm not seeing the correct way to do this if the base doesn't have an associated table and the subclasses don't have a specific column that is different between the table definitions.
You have to use one of three available inheritance mappings strategies. From your description, you should consider using table-per-concrete-class mapping, or change your db scheme.
You can find more information about pros and cons of strategies here: https://www.hibernate.org/hib_docs/nhibernate/html/inheritance.html.