Spring data - force merge of manytoone element when inserting main entity - java

So I have a bit of an annoying database layout, where I have to have a dynamic list side table which uses a natural key. And this second table should get a new entity added if it doesn't exist yet, otherwise updated.
So to say:
citizen
city
id
code
postal_code
name
The Citizen entity has
#ManyTone(cascade = javax.persistence.CascadeType.ALL)
#Cascade(CascadeType.SAVE_UPDATE)
#JoinColumn(name = "postal_code"
private City city;
Now when I create a new Citizen and City instance, and insert them it works fine the first time. However the second time I get a PK violation on city since there's already a city with the given code. Now what I want to achieve is that the city gets saved or updated when the citizen gets saved. (not the best example I know)
To persist I used the 'JpaRepository -> saveAll' method.
Worst case I could ofcourse first persist all cities, and then set the persisted instances on the citizen but I'd like to avoid that.
The side entities to get created/updated as passed in without first having to manually insert them myself.

I think you will need to verify if the city exists before persist a new Citizen, and if it exists, you will need to set the City property with the object you get from the database.
Otherwise, it will try to persist a new city with the same key, and it will throw the PK violation error.

Related

Is there a way of using a lookup table as a converter in JPA?

Imagine that I have a simple entity as follows:
#Entity
#Table(name = "PERSON")
public class Person {
#Id
#Column(name = "NAME")
private String name;
#Column(name = "GENDER")
private String gender;
}
And two tables, the actual table holding the information and a lookup table.
TABLE PERSON (
NAME VARCHAR2 NOT NULL,
GENDER INT NOT NULL);
TABLE GENDER_LOOKUP (
GENDER_ID INT NOT NULL,
GENDER_NAME VARCHAR2 NOTNULL);
I want to save the information from my entity into the table, so that the String field gender is automatically converted to the corresponding gender int, using the lookup table as a reference. I thought of two approaches, but I was wondering if there was a more efficient way.
Create an enum and use ordinal enum to persist. I would rather avoid this because I'd like to have only one "source of truth" for the information and for various business reasons, it has to be a lookup table.
Use the #Converter annotation and write a custom converter. I think that this would require me to query the table to pull out the relevant row, so it would mean that I would have to make a JPA call to the database every time something was converted.
I'm currently planning to use 2, but I was wondering if there was any way to do it within the database itself, since I assume using JPA to do all of these operations has a higher cost than if I did everything in the database. Essentially attempt to persist a String gender, and then the database would look at the lookup table and translate it to the correct Id and save it.
I'm specifically using openJpa but hopefully this isn't implementation specific.
Since you seriously considered using enum, it means that GENDER_LOOKUP is static, i.e. the content doesn't change while the program is running.
Because of that, you should use option 2, but have the converter cache/load all the records from GENDER_LOOKUP on the first lookup. That way, you still only have one "source of truth", without the cost of hitting the database on every lookup.
If you need to add a new gender1, you'll just have to restart the app to refresh the cache.
1) These days, who know what new genders will be needed.

How can I make use of uniqueConstraints for multiple columns in hibernate?

I have a task to create database, where I could be able to store information about currency bid and ask values from particular days. My entity has few fields, two of them are currencyType (enum USD, EUR and so on) and currencyDate (LocalDate). Both of them should not be unique, but their merge should be (I want to use query.uniqueResult() to get currency from database). Thats why I used:
uniqueConstraints = #UniqueConstraint(columnNames = {"currencyType", "currencyDate"}))
I though that everytime I try to add to database already existing combination of currencyType and currencyDate, the database will just ignore the query, but it gives me an error, eg:
ERROR: Duplicate entry '0-2017-10-27' for key 'UK56cur2s470vruol04nntdbl49'
where first 0 stands for currencyType(in int) and other part is date. How can I make hibernate work the way I want? I mean to pass the query, when it would duplicate the record?
The only thing that I know to get behavior like you want is to make your combination of this two fields as Id and to use merge or saveOrUpdate methods for storing, but I don't think that it is good design. As for me the best way of solving this is to fetch record from db by this two values and update it if present or create new if not.

Hibernate Insert Options

I am trying to efficiently implement a Hibernate connection in Java and recently came across two ways of adding database rows using Hibernate which I would like to discuss:
Given are the following SQL Tables:
CREATE TABLE Customer (
customer_id INT NOT NULL AUTO_INCREMENT,
customer_name VARCHAR(255) NOT NULL UNIQUE KEY,
PRIMARY KEY (customer_id)
) ENGINE=InnoDB;
CREATE TABLE Orders (
order_id INT NOT NULL AUTO_INCREMENT,
customer_name VARCHAR(255) NOT NULL,
order_date DATETIME,
PRIMARY KEY(order_id),
FOREIGN KEY (customer_name) REFERENCES Customer(customer_name)
ON UPDATE CASCADE
ON DELETE CASCADE
) ENGINE=InnoDB;
Customer is parent table and Orders is child table, 1:n relationship.
When I want to insert new Orders for a certain customer, which is already in the database I know to options how to achieve it :
(1)
// Saving the child table
// Query the customer
Customer cu = (Customer) session.get(Customer.class, 1);
Orders od = new Orders(cu, new Date());
session.save(od);
// This seems to work fast and efficient
(2) Saving the parent table
// Due to bidirectional relationship one can also do:
Orders od = new Orders(cu, d);
cu.getOrderses().add(od);
session.save(cu);
// This is what is often shown in Hibernate tutorials, but seems to be really
// inefficient because the entire HashSet of the parent table needs to loaded //first,
//before the new object can be added.
Benchmark example : (1) 0 sec (2) 5 sec
Why is way (2) often shown in Tutorials even though it seems to be inefficient ?
Which option you should choose depents on your model. Do you have/need a collection of orders in the Customer? Then you have to add the object anyway and you should choose (2).
Take care of proper fetch configuration or otherwise Hibernate will load the whole collection even if you don't need it. For that case take a look at Hibernate - How to persist a new item in a Collection without loading the entire Collection.
If you don't have a collection in Customer then use option (1).
(2) is often shown in tutorials to show the capabilities of Hibernate/ORMs in general. It's also more object oriented but I would recomment to create a method Customer.addOrder() or even Customer.creatOrder() instead od directly manipulating the collection from outside.

JPA throwing "multiple assignments to same column" during save operation

I have a model class that references another model class and seem to be encountering an issue where the #OneToOne annotation fixes one problem but causes another. Removing it causes the inverse.
JPA throws "multiple assignments to same column" when trying to save changes to model. The generated SQL has duplicate columns and I'm not sure why.
Here's a preview of what the classes look like:
The parent class references look like this:
public class Appliance {
public Integer locationId;
#Valid
#OneToOne
public Location location;
}
The child Location class has an id field and a few other text fields -- very simple:
public class Location {
public Integer id;
public String name;
}
When I attempt to perform a save operation, does anyone know why JPA is creating an insert statement for the Appliance table that contains two fields named "location_id"?
I need to annotate the reference to the child class with #OneToOne if I want to be able to retrieve data from the corresponding database table to display on screen. However, If I remove #OneToOne, the save works fine, but it obviously won't load the Location data into the child object when I query the db.
Thanks in advance!
It appears you did not define an #InheritanceType on the parent Class. Since you did not, the default is to combine the the parent and the child class into the same Table in the Single Table Strategy.
Since both entities are going into the same table, I think that #OneToOne is trying to write the id twice - regardless of which side it is on.
If you want the parent to be persisted in its own table, look at InheritanceType.JOINED.
Or consider re-factoring so that you are not persisting the parent separately as JOINED is not considered a safe option with some JPA providers.
See official Oracle Documentation below.
http://docs.oracle.com/javaee/7/tutorial/doc/persistence-intro002.htm#BNBQR
37.2.4.1 The Single Table per Class Hierarchy Strategy
With this strategy, which corresponds to the default InheritanceType.SINGLE_TABLE, all classes in the hierarchy are mapped to a single table in the database. This table has a discriminator column containing a value that identifies the subclass to which the instance represented by the row belongs.
In OpenJPA, according to the docs (http://openjpa.apache.org/builds/1.0.1/apache-openjpa-1.0.1/docs/manual/jpa_overview_mapping_field.html), section 8.4, the foreign key column in a one-to-one mapping:
Defaults to the relation field name, plus an underscore, plus the name
of the referenced primary key column.
And the JPA API seems to concur with this (http://docs.oracle.com/javaee/6/api/javax/persistence/JoinColumn.html)
I believe this means that in a one-to-one mapping, the default column name for properties in a dependent class is parentClassFieldName_dependentClassFieldName (or location_id in your case). If that's the case, the location_id column you are defining in your Appliance class is conflicting with the location_id default column name which would be generated for your Location class.
You should be able to correct this by using the #Column(name="someColumnName") annotation and the #JoinColumn annotation on your #OneToOne relationship to force the column name to be something unique.
Ok gang, I figured it out.
Here's what the new code looks like, followed by a brief explanation...
Parent Class:
public class Appliance {
public Integer locationId;
#Valid
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name="location_id", referencedColumnName="id")
public Location location;
}
Child Class:
public class Location {
public Integer id;
public String name;
}
The first part of the puzzle was the explicit addition of "cascade = CascadeType.ALL" in the parent class. This resolved the initial "multiple assignments to same column" by allowing the child object to be persisted.
However, I encountered an issue during update operations which is due to some sort of conflict between EBean and JPA whereby it triggers a save() operation on nested child objects rather than a cascading update() operation. I got around this by issuing an explicit update on the child object and then setting it to null before the parent update operation occurred. It's sort of a hack, but it seems like all these persistence frameworks solve one set of problems but cause others -- I guess that's why I've been old school and always rolled my own persistence code until now.

Does it make sense to create entity table with only ID attribute?

Does it make sense to create a single entity when it should only contain the #Id value as a String?
#Entity
class CountryCode {
#Id
String letterCode; //GBR, FRA, etc
}
#Entity
class Payment {
CountryCode code;
// or directly without further table: String countryCode;
}
Or would you just use the letterCode as the stringvalue instead of creating the CountryCode entity?
It should later be possible for example to fetch all payments that contain a specific countrycode. This might be possible with both solutions. But which is the better one (why)?
Yes you can if you are using the entity as a lookup. In your example, you may want to add a column for description congaing (France, Great Britain, etc.) for the letter code and a third column whether it is active or not and maybe columns for when inserted and when it was last changed.
It makes sense to create such table to provide consistency of data, that is that no Payment is created with non-existing CountryCode. Having a separate entity (that is table) together with foreign key on Payment allows checking for consistency in database.
Another possible approach is to have check constraint on the code field but this is error prone if codes are added/deleted and/or there are more than one column of this type.
Adding the letterCode the the Payment Class as String Attribute (Or Enum to prevent typo errors) will increase the fetch performance as you do not need to create a join over your CountryCode Table.

Categories

Resources