How to model lookup tables JPA - java

I'm trying to build a Spring Boot data layer on top of another project's DB. I'm want to get to a point where I can consume their data via Restful endpoints rather than directly from the DB. Maximum abstraction is the goal. Here's my problem. Consider the following JPA entity:
#Entity
#Table(name = "PERSON", schema = "public")
public class Person {
#Id private long id;
private String name;
private long favoriteFood;
private Address address;
//Getters, Setter etc.....
}
Notice that favoriteFood is a long, not a String. This is because the DB uses a lookup table. Let's say Joe's favorite food is pizza. The person table stores a 1 in the favorite_food column which is the fk to the "pizza" value stored in the food_ref table. This pattern is repeated hundreds or times in the DB. What is the best way to model this in JPA/Hibernate? Change the variable to String and have the getter and setter do the lookup? I've not found any examples which seems strange. This is a common DB structure. Any advice on best practices would be appreciated. Thanks!

The best way in this scenario is to use one to one relationship in the JPA entity with the FoodRef class
#Entity
#Table(name = "PERSON", schema = "public")
public class Person {
#Id private long id;
private String name;
#OneToOne(fetch=FetchType.LAZY)
#JoinColumn(name="food_ref_id")
private FoodRef favoriteFood;
private Address address;
//Getters, Setter etc.....
}

Related

Crnk JsonApiRelation, OneToMany and filtering implementation

I use crnk (JSON-API) in java project and I have 3 questions regarding its usage with spring boot and jpa - haven't found exact implementation details in documentation.
For example, I have 2 entities and respective tables:
#Entity
#JsonApiResource(type = "employee")
public class Employee {
#Id
#JsonApiId
private int id;
private String name;
#ManyToOne
#JoinColumn(name = "typeId")
private EmployeeType employeeType; //stored in table as typeId
}
#Entity
#JsonApiResource(type = "type")
public class EmployeeType {
#Id
#JsonApiId
private int id;
private String typeName;
private int salary;
}
How should JsonApiRelation be introduced in order to be able to call "/employee/1" and "/employee/1/type" urls?
For example there is one more entity.
#Entity
#JsonApiResource(type = "project")
public class Project {
#Id
#JsonApiId
private int id;
private String supervisorName;
private String projectName;
}
First, I'd like to have List of Projects for each Employee, where he is a supervisor, joint by name and have it listed as attribute in Json.
Tried implementing it with #OneToMany and #JoinColumn annotations but got StackOverflowException. How could this be implemented. And second, how could this be implemented with Relation? Like "/employee/1/projects" url.
How should I implement custom filtering of results for findAll method? For example, I have a List of all Employees, but I'd like to exclude some of them from the response. Which class/method should be introduced for this behaviour?
#JsonApiRelation annotation should not be necessary. Crnk will detect the #ManyToOne annotation and map it accordingly.
in case of crnk-jpa it is sufficient to specify all relationships in JPA. Matching JSON API relationships. So your approach seems good. What was the StackoverflowException stacktrace? (next to the examples, there are also many example entities in crnk-jpa)
I would make use of a decorator. See http://www.crnk.io/documentation/#_request_filtering. RepositoryDecoratorFactory allows to place a custom repository between the caller and crnk-jpa (or any other kind of repository). There you can do any kind of modification perform (maybe) calling the "real" repository. => Will add an example for this
feel free also make open up tickets in crnk for any documentation/example clarifications.

Separate Table vs Extra Columns in JPA/Hibernate

I am wondering about best practices in database design with Hibernate.
I have a User entity that is going to have a lot of different settings. For each set of settings, I have to either add them as extra columns in the User table or create a separate entity and connect them with a #OneToOne relationship. It is my understanding that #OneToMany and #ManyToOne relationships should generally take place in separate tables because you should not have columns that are optional.
But it is kind of unclear for #OneToOne relationships. I think there is a case for using #OneToOne because ORMs will select all single attributes by default and having a lot of columns will slow down that process.
An example of what I am talking about can be illustrated by
#Entity
public class User{
#OneToOne
private ForumSettings forumSettings;
#OneToOne
private AccountSettings accountSettings;
#OneToOne
private SecuritySettings securitySettings;
}
vs
#Entity
public class User{
#Column
private boolean showNSFWContent; //Forum Setting
#Column
private int numberOfCommentsPerPage; //Forum Setting
#Column
private boolean subscribedToNewsLetter; //Account Setting
#Column
private boolean isAccountBanned; //Account Setting
#Column
private boolean isTwoFactorAuthenticationEnabled; //Security Setting
#Column
private boolean alertForSuspiciousLogin; //Security Setting
}
The above is a simple example to show the concept, but in practice there would be many more columns in the 2nd portion.
I know that this might be opinion based, but I am hoping someone could share the pros/cons of both choices.
Thank you very much
Your question is in general about Data normalization. Normalization is itself extensive field of study and basically is a way of structuring database tables avoiding redundancy and making sure that updates don’t introduce anomalies.
And first rule of normalization says a table shall contain no repeating groups. In your case it does.
SOLUTION 1 : Store UserSettings as Entity as map as OneToMany relationship
#Entity
public class User
#OneToMany
private List<UserSettings> userSettings;
And then you can query for particular setting type by joining User and UserSettings entities.
For example (JPQL)
SELECT user u
JOIN u.settings us
WHERE us.settings_type = 'account_settings'
and us.settings_value = 'secure' // or any other logic
Advantage of this approach is that UserSettings will have it is own persistence identity and can be queried by it's own. It it is not dependent on parent.
For example :
SELECT q from Query q where ...
Solution 2 : Store settings in a collection of basic elements
You can store User Settings in the collection (Each user will have it's own set of settings)
#Entity
public class User {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
private String name;
...
#ElementCollection
#CollectionTable(name="USER_SETTINGS")
#MapKeyColumn(name="SETTINGS_TYPE")
#Column(name="SETTINGS_VALUE")
Map<String, Boolean> userSettings = new HashMap<>();
UserSettings collection will be stored in a separate table with foreign key to User table. UserSettings does not have it is own persistence ID, is dependent on User entity and can be queried only through it is parent ('User')
Solution 3: Store User Settings as Embedded type
Embedded type is not an entity, it does not have it is own persistence ID and is depends on parent type, stored as part of parent record in database (in User table)
#Entity
public class User {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
private String name;
...
#Embedded
private UserSettings userSettings;
UserSettings is in separate class, but stored in User table.
#Embeddable
public class UserSettings {
private List<String> securitySettings; // or any other collection type
private List<Boolean> forumSettings;

How to create correct entities mapping?

I would like to use ORM Hibernate. And faced with issue below.
There are few tables that have translation on different languages. e.g. it looks like this:
In the database I can use join and get all required data
e.g.:SELECT * FROM car c
JOIN translation t on c.description = t.description
WHERE t.type = 'CAR'
As you can see table 'translation' has column 'type' that uses for filtering by type.
So the main question how to create correct entities and mapping?
A car has OneToMany translations. Similary a city has OneToMany translation.
But every translation has zero to one car or zero to one city.
A translation is either for a Car or a City because its description talks about either City or Car. This is achieved by ManyToOne notation. There is no annotation in hibernate to explicitly say the range from Zero. Following is just to get an Fair Idea of mappings. I am not assuming any relation between City and Car even though I can think city has OneToMany cars.
#Entity
class Car
{
private int Id;
private String name;
//.......... declare all variables that you need
#OneToMany(mappedBy = "car")
private List<Translation>
//getters and setters
}
#Entity
class City
{
private int Id;
private String name;
//.......... declare all variables that you need
#OneToMany(mappedBy = "city")
private List<Translation>
//getters and setters
}
#Entity
class Translation
{
private int Id;
private String language;
//.......... declare all variables that you need
#ManyToOne
private Car;
#ManyToOne
private City;
//getters and setters
}

JPA: Foreign key that is also a primary key mapping

I have been trying to solve this for whole day but no luck! Also i tried to read most of the tutorials on the net but as you all know they all are full of useless examples that do not reflect what you need in the real world.
So here is my situation:
The database:
table: vehicles(vehicleId, brand, model, devYear, regNumber) <-- vehicleId is the PrimaryKey
table: extras(vehicleId, allowSmoke, allowFood, allowDrinks, airConditioner) <-- vehicleId is a PK and a FK.
The point is that if i have a class Vehicle and a class TravelExtras which are mapped to the database i want the Vehicle class to have an attribute TravelExtras travelExtras and get and set methods.
Unfortunatelly no matter what i tried when i try to persist the object in the databse i get various errors.
Here is an illustration:
EntityManagerFactory emfactory = Persistence.createEntityManagerFactory( "NaStopPU" );
EntityManager entitymanager = emfactory.createEntityManager( );
entitymanager.getTransaction( ).begin( );
TravelExtra travelExtra = new TravelExtra();
entitymanager.persist(travelExtra);
Vehicle vehicle = new Vehicle(2L, "10152487958556242", "Mazda", "626", "334343", 2005, 4);
vehicle.setTravelExtra(travelExtra);
entitymanager.persist(vehicle);
entitymanager.getTransaction().commit();
entitymanager.close( );
emfactory.close( );
Any one knows what kind of annotations to use for this One to one case ?
The Java Persistence wikibook has a section called Primary Keys through OneToOne and ManyToOne Relationships which seems to indicate that what you want is possible.
If I'm reading it right, for your case, it would look something like:
class Vehicle {
#Id
#Column(name = "EXTRAS_ID")
private long extrasId;
#OneToOne(mappedBy="vehicle", cascade=CascadeType.ALL)
private TravelExtra extras;
}
class TravelExtras {
#Id
#Column(name = "VEHICLE_ID")
private long vehicleId;
#OneToOne
#PrimaryKeyJoinColumn(name="VEHICLE_ID", referencedColumnName="EXTRAS_ID")
private Vehicle vehicle;
public TravelExtras(Vehicle vehicle) {
this.vehicleId = vehicle.getId();
this.vehicle = vehicle;
}
}
Note that one of your entities will need to make sure it has the same id as the other, which is accomplished in the example by the TravelExtras constructor requiring the Vehicle it is bound to.
I know this is very old qs, but for completeness of your case
you can just have (jpa 2.0)
#Entity
#Data
public class Vehicle implements Serializable{
#Id
#GeneratedValue
private long vehicleId;
.. //other props
}
#Entity
#Data
public class VehicleExtras implements Serializable{
#Id
#OneToOne (cascade = CASCADE.ALL)
#MapsId
#JoinColumn(name ="vehicleId")
private Vehicle vehicle;
#Column
private boolean allowSmoke;
..// other props.
}
should share same pk/fk for VehicleExtra table
Why don't you use an #Embedded object? When using an embedded object, you get
the logical separation you desire in your code and keep your database compliant with Entity-Relational Normalization rules.
It's weird to think on a One-to-One relationship, because even though JPA/Hibernate allows it, all data should be stored in the same table, making you model simpler, while also simplifying queries and increasing database performance by removing the need for a Join operation.
When using Embedded objects you don't have to worry about mapping IDs and bizarre relations, since your ORM is capable of understanding that your just making a code separation, instead of demanding an actual relation of One-to-One between tables.
class Vehicle {
#Id
#Column(name = "ID")
private long vehicleId;
#Column(name = "BRAND")
private String brand;
#Column(name = "MODEL")
private String model;
#Column(name = "DEV_YEAR")
private int devYear;
#Column(name = "REG_NUMBER")
private int regNumber;
#Embedded
private TravelExtra extras;
// Constructor, getters and setters...
}
.
#Embeddable
class TravelExtras {
#Column(name = "ALLOW_SMOKE")
private boolean allowSmoke;
#Column(name = "ALLOW_FOOD")
private boolean allowFood;
#Column(name = "ALLOW_DRINKS")
private boolean allowDrinks;
#Column(name = "AIR_CONDITIONER")
private boolean airConditioner;
// Default Constructor, getters and setters...
}
You can map your classes for example with Netbeans. It will generate annotations. The problem could be your dao layer. You have to persist objects in correct way. For example can't save travelExtra without Vehicle. Also be aware of owning side.

hibernate different types of one-to-one relationships on a single class

I am trying to figure out the best way to accomplish a relationship in hibernate. I have a Customer object. Each customer has a technical contact, a billing contact, and a sales contact. Each type of contact has the exact same data structure (phone, email, address, etc).
My first thought was to create a Contact table, and then have three columns in the Customer table - sales_contact, billing_contact, technical_contact. That would make three distinct foreign key one-to-one relationships between the same two tables. However, I have found that this is very difficult to map in Hibernate, at least using annotations.
Another thought was to make it a many to many relationship, and have a type flag in the mapping table. So, any Customer can have multiple Contacts (though no more than three, in this case) and any Contact can belong to multiple Customers. I was not sure how to map that one either, though. Would tere be a type field on the map table? Would this attribute show up on the Contact java model object? Would the Customer model have a Set of Contact objects. or three different individual Contact objects?
So I am really looking for two things here - 1. What is the best way to implement this in the database, and 2. How do I make Hibernate map that using annotations?
It can be as simple as :
#Entity
public class Contact {
#Id
private String id;
private String phome;
private String email;
private String address;
// ... Getters and Setters
}
#Entity
public class Customer {
#Id
#GeneratedValue
private String id;
#ManyToOne
#JoinColumn(name = "ID")
private Contact billingContact;
#ManyToOne
#JoinColumn(name = "ID")
private Contact salesContact;
#ManyToOne
#JoinColumn(name = "ID")
private Contact technicalContact;
public Customer() {
}
// ... Getters and Setters
}
Now, if you want to make the difference between a BillingContact and a SalesContact at the object level, you can make Contact abstract, and implement it with each type of contact. You will have to annotate the parent class with #Inheritance to specify the inheritance strategy of your choice (SINGLE_TABLE sounds appropriate here, it will use a technical discriminator column - see http://docs.jboss.org/hibernate/annotations/3.5/reference/en/html_single/#d0e1168).
How about using #OneToOne and just naming the #JoinColumn differently for each type:
#Entity
public class Contact {
#Id
private String id;
private String phone;
private String email;
private String address;
// ... Getters and Setters
}
#Entity
public class Customer {
#Id
#GeneratedValue
private String id;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="billingContact_ID")
private Contact billingContact;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="salesContact_ID")
private Contact salesContact;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name="technicalContact_ID")
private Contact technicalContact;
public Customer() {
}
// ....
}
For each row in Customer table should create three rows in Contact table

Categories