I ran some DDL script to setup a complete country table in my database. The country table's primary key column contains the corresponding ISO code for every country.
In my JPA project I have a User entity having an embedded Address entity and this Address entity has a reference to a Country. The relationship between User and Address seems to be no problem to me, but the relationship between Address and Country. I tried to map it as a ManyToOne relationship, since many addresses can share a country.
Problem is: I annotated the iso member variable of the Country class with Id -> Now, JPA/Hibernate complains about not having set the id of the country manually. But in this case, the id is already given and set, since I imported the data once and the ISO code is unique and by db schema means declared as primary key. In this special case, there is no need for updates or inserts in the country table - the information should be read only!
Any idea what to do, so I can use my countries table without altering?
Your question is missing some details, so the following involves a lot of guessing :-)
Your Country class should look something like:
#Entity
#Immutable
#Table(name="countries")
public class Country {
#Id
private String isoCode;
// all other attributes, getters / setters, etc...
}
#Immutable is a Hibernate extension to JPA standard; you don't have to put it on entity but having it will result in slightly better performance. Keep in mind that it will really make the Country immutable - you won't be able to create / update / delete countries through your application. You may want to configure cache for your Country entity as well if it's used often enough.
Your Address would have the following mapping to country:
#ManyToOne
#JoinColumn(name="country_iso_code")
private Country country;
Note the absence of "cascade" attributes - you don't need any. The final important point here is you actually need to get or load the Country instance to set it on address:
Country country = (Country) session.load(Country.class, isoCode);
// OR
Country country = (Country) session.get(Country.class, isoCode);
address.setCountry(country);
...
session.saveOrUpdate(address);
The first line above will not hit the database; use it if you know that country with such an ISO code exists. Second form will hit the database (or cache, if configured) and return NULL if country with that code does not exist.
Related
I am reading data from old legacy dabatase. Somehow, they place all non-existing relationship using 0 index, for example:
Object Person:
id: 123
name: john
surname: snow
birthCityId: 0 <-- this means that there is no relationship between city and this person.
Now, in JPA I have the problem that it is loading person entity but it cannot find related city entity with index 0. I would like to code that when I have city with ID 0, then City entity is set to null.
How can I do that?
I don't want to create a new entity with index 0 into the db.
Thanks
You can use the Hibernate #NotFound annotation:
#ManyToOne
#NotFound(action=NotFoundAction.IGNORE)
private City birthCity;
https://docs.jboss.org/hibernate/orm/5.3/javadocs/index.html?org/hibernate/annotations/NotFound.html
I do not see the other posted solutions working as an exception will occur on Hibernate load i.e. prior to you being able to handle it by other means.
I am asuming u have
Person{
#Many2One #JoinColumn("birthCityId") City birthCity;
...}
the easiest solution is to add table row with id=0 in city and the rest be nulls
and this to your class
#PostLoad
public viod cityInit(){
if(birthCity!=null&&birthCity.getId()==0){
birthCity==null;
}
}
There are more elegant solutions but this will get you started quickly
I have an old database where there are two tables with implicit association between them:
booking
- id
- name
... some other fields...
and
booking_info
- id
- booking_id
... some other fields...
Due to the current database design there no any constraints between these two tables, which means that one booking entry may exist without any booking_info entries and vice versa. booking_id in booking_info table is an implicit foreign key which refers to booking table (column id), but it also may refer to the absent booking.
I have created the following JPA mapping for these tables:
#Entity
public class Booking {
#Id
private Long id;
private String name;
// getters & setters
}
and
#Entity
public class BookingInfo {
#Id
private Long id;
#OneToOne
#JoinColumn(name = "booking_id")
private Booking booking
// getters & setters
}
Now I need to be able to persist a BookingInfo entity even if there's no related Booking entry in the database.
BookingInfo bookingInfo = new BookingInfo();
bookingInfo.setId(1);
Booking booking = new Booking();
booking.setId(182); // let's say that there's no booking with id 182 in my database, but I still need to persist this bookingInfo
bookingInfo.setBooking(booking);
bookingInfoRepository.save(bookingInfo); // using Spring Data JPA
If I run this code then I get javax.persistence.EntityNotFoundException since booking with id 182 is absent.
What would be the proper workaround for my case using JPA or Hibernate.
Btw, I also tried to use Hibernate's #NotFound annotation. As a result, save method doesn't throw javax.persistence.EntityNotFoundException and entity gets persisted int the database, but the problem is that booking_id in the database always null.
Any help would be appreciated.
I am not sure my answer will help you or not, but the result you are getting perfectly make sense. As you are setting a JPA object, and that object is not present, hence the null value is saved.
If you want to save 182 as an integer, you don't do JPA relationship. Instead, you just use booking-id as an integer field in booking-info. And that makes sense because you actually do not have the relationship between those tables which the JPA is trying to achieve.
But I am sure you just want to save 182 and as well as maintain the JPA relationship. And I am sure you already know it, but DB integrity is not being maintained with the approach you are taking. I am sure there is enough reason behind that. But my recommendation would be applying proper constraints in the DB and then in JPA.
I have a Person entity with an embeddable Address and there's a one-to-many relation between them (a person can have multiple addresses). The current mapping is something like this:
#Embeddable
public class Address {
// ... attributes
}
#Entity
public class Person {
#ElementCollection(fetch = FetchType.EAGER)
#JoinTable(name = "Person_addresses", joinColumns = #JoinColumn(name = "personid")
)
/*
Attribute ovverrides with annotations
*/
private java.util.Set<Address> addresses = new java.util.HashSet<Address>();
}
Using this annotation means that in the database I have a Person_addresses table which contains all the address attributes and a personid. But it also means that if I have a person with an address list and I update the address list, Hibernate deletes all the related records and inserts them (the modified ones) again.
As far as I know there's a way to have a primary key in this table for each record - in this case hibernate can decide which item of the list needs to be updated. So my question is, how can I map an embeddable list with identifiers in the joining table? (I hope it's understandable what I want:)).
http://en.wikibooks.org/wiki/Java_Persistence/ElementCollection#Primary_keys_in_CollectionTable
The JPA 2.0 specification does not provide a way to define the Id in
the Embeddable. However, to delete or update a element of the
ElementCollection mapping, some unique key is normally required.
Otherwise, on every update the JPA provider would need to delete
everything from the CollectionTable for the Entity, and then insert
the values back. So, the JPA provider will most likely assume that the
combination of all of the fields in the Embeddable are unique, in
combination with the foreign key (JoinColumn(s)). This however could
be inefficient, or just not feasible if the Embeddable is big, or
complex. Some JPA providers may allow the Id to be specified in the
Embeddable, to resolve this issue. Note in this case the Id only needs
to be unique for the collection, not the table, as the foreign key is
included. Some may also allow the unique option on the CollectionTable
to be used for this. Otherwise, if your Embeddable is complex, you may
consider making it an Entity and use a OneToMany instead.
So thats it, it can't be done.
As maestro's reply implies, the only portable solution is to convert this to use an entity and a one-to-many.
That said, Hibernate has a non-spec feature called an "id bag" which allows you to map a basic or embeddable collection with an identifier for each row, thereby giving you the efficient updates you want:
#Entity
public class Person {
#CollectionId( columns={"address_id"}, type="int", generator="increment" )
#ElementCollection(fetch = FetchType.EAGER)
#JoinTable(name = "Person_addresses", joinColumns = #JoinColumn(name = "personid"))
private java.util.List<Address> addresses = new java.util.ArrayList<Address>();
}
Notice the switch from Set to List however. Also notice the generated table structure... looks an awful lot like an entity ;)
The Hibernate documentation states the following:
5.1.6.5. Mapping one entity to several tables
While not recommended for a fresh schema, some legacy databases force
your[sic] to map a single entity on several tables.
I have done a lot of reading on database normalization, and I don't really understand how you can both normalize a database and NOT map an entity on several database tables, unless you map several different entities and do a weird join statement manually. Or, you map 7 different entities and transfer them to one POJO. Is there somewhere that shows how to develop a "fresh schema" that is both normalized and takes a single entity to turn from database data to a java object?
Also, I understand that complete normalization isn't necessary in today's world of cheap storage, but I just found this statement to be at odds with everything else I've read. I am looking for a balance of normalized to de-normalized, but haven't found a simple way with the the Java Persistence API.
EDIT:
Example:
If I have a user Entity with the following:
#Entity
public class User {
long id;
String name;
String email;
int countryCode;
List<Images> uploadedImages;
}
I am not going to store the user's country name and collection of images on the same table, rather I will have the country on one table:
Table Countries Country_Code Country_Name
AF Afghanistan etc....
with country code and country name, then on , and uploaded images on a separate table with user id's,
Table UploadedImages User_Id Image_Name Image_Url
1 Hello.jpg Amazon S3
1 Goodbye.jpg Photobucket
So how do I do this with only one entity? Or do I get three separate entities from the database using information from the first entity? As you can tell, I'm a bit confused on the basic schema, How would I translate the data above to a java object?
Given your example
#Entity
public class User {
long id;
String name;
String email;
int countryCode;
List<Images> uploadedImages;
}
Here you have 3 entities - a User, a Country and an Image entity with each one probably mapping to 3 tables - one entity per table and a join table for the list of images.
So your User class then becomes
#Entity
public class User {
long id;
String name;
String email;
Country country
List<Images> uploadedImages;
}
#Entity
public class Country {
long id;
String name;
}
#Entity
public class Image{
long id;
String name;
String url;
}
And you'd add annotations to map the properties to the correct tables and columns.
What the documentation says has nothing to do with data normalization. This has to do with data partition/table optimization. So, if you have a table with a column that holds a big binary, you probably don't want that to be part of the same table as the main data. This was more common in the past, as data considered small by today's measures were considered "big" back then.
Normalization, on the other hand, is very welcome in OOP (and Hibernate). It's all about keeping things on their places, to minimize redundancy and dependency
I'm not 100% sure this is only a Hibernate issue as this might be a more abstract decision but I'll give it a try.
Since the problem description is a bit lengthy I'll first state that what I'd like to do is see if I can change the implementation to something which more resembles a Best practice implementation then this.
I have 3 entities, relevant to this issue: Workstation (ws), Employee and Organization-unit(org-unit).
An Employee can belong to one org-unit.
An Org-unit can hold many employees.
A Workstation is used to display data of an Org-unit(in general), Of an Org-unit and a specific employee in it and an employee which does not belong to an org-unit.
Currently, for various reasons which were out of my control, we do not use any associations between the entities in Hibernate or via DB-Constraints but we just use Columns which logically serve as Foreign-Keys.
We currently have an additional table which has 4 columns:Id,WSId,EmployeeId,OrgUnitId.
This allows a WS to refer to an orgunit (where employeeId is null),to an employee without an org-unit (orgunitId is null) or to an employee and org-unit (where none are null).
I'd like to be able to know:
1.Given a WS, which employees is it following and which org-units and how (i.e., alone, with an employee? which?)
2.Given an employee, which WS are monitoring it.
3.Given an org-unit, which WS are monitoring it and how (i.e., alone, with an employee? which?)
This issues relates to the Presentation layer as it dictates the view will be generated BUT it is a part of the domain model as a user, will use, an interface to manipulate these monitoring mappings and so these mappings are a part of the domain model.
I'm not sure if what I have is not the least evil among options, and I would greatly appreciate comments and suggestions.
EDIT From one of the answers I think it is not clear enough that a WS can display data for many such mappings at the same time, in a mixture of the above sorts (org-unit, employee etc.)
OK, I don't know how to implement this on the database side, but here is an Entity Model that should cover the relationship you are talking about.
Edit:
This is a new version in response to your comments. Now every WorkStation has n bindings each of which can have employee or orgunit or both (use DB constraints to ensure they don't have neither).
You can also access the bindings per orgunit and per employee, which should make the above queries much easier:
#Entity
public class OrgUnit{
#OneToMany(mappedBy="orgUnit")
private Set<Binding> bindings;
}
#Entity
public class Employee{
#OneToMany(mappedBy="employee")
private Set<Binding> bindings;
}
#Entity
public class Binding{
#ManyToOne(optional = true)
private Employee employee;
#ManyToOne(optional=true)
private OrgUnit orgUnit;
#ManyToOne(optional=false)
private WorkStation owner;
}
#Entity
public class WorkStation{
#OneToMany(mappedBy="owner")
private Set<Binding> bindings;
}
Sample Client code:
public Set<WorkStation> getWorkstationsPerEmployee(final Employee employee){
final Set<WorkStation> workStations = new HashSet<WorkStation>();
for(final Binding binding : employee.getBindings()){
WorkStation workStation = binding.getOwner();
if(workStation!=null)
workStations.add(workStation);
}
return workStations;
}
Sounds like all you really need is a nullable FK on Employee to OrgUnit, and two nullable FKs on WS to both Employee and OrgUnit. To see which WS are monitoring an employee, just get all the WS with matching emp_id columns. Same with the WS monitoring an OrgUnit, possibly with the additional stipulation of emp_id being null or not (depending on if you need to handle those situations separately). No idea where "patients" fits in, you didn't give any details about that.