using composite key with hibernate - java

Suppose i want to have a composite key as street, city for purchase order entity.
Below is how i identify doing it,
#Embeddable
public class BillingAddress implements Serializable {
private String street;
private String city;
public BillingAddress(){
}
public BillingAddress(String street, String city) {
this.street = street;
this.city = city;
}
//with getters and setters
}
#Entity
#IdClass(BillingAddress.class)
public class PurchaseOrder {
public PurchaseOrder(BillingAddress billingAddress) {
street = billingAddress.getStreet();
city = billingAddress.getCity();
}
#Id
#AttributeOverrides({
#AttributeOverride(name = "street", column = #Column(name = "STREET")),
#AttributeOverride(name = "city", column = #Column(name = "CITY")) })
private String street;
private String city;
private String itemName;
public String getItemName() {
return itemName;
}
public void setItemName(String itemName) {
this.itemName = itemName;
}
}
I want to understand what is really #AttributeOverrides annotation do? Even i change to colomn name to something STREET1 i still see the table created with column name STREET. So what is column = #Column(name = "STREET")) doing here.
Also instead of constructore taking the BillingAddress i can have it like a field of PurchaseOrder class right like,
public class PurchaseOrder {
BillingAddress billingAddress;
}
In this case how this going to change?
Do i still need to have private String street; private String city; in PurchaseOrder?
Finally i read that using composite keys should be avoided in new data base system design which using composite primary key is applicable a situation where in order to map the legacy data base tables with out changing the data base table structure right? Is that statement a valid one?
//Edit question
Saving purchase order which billing address is in the field,
PurchaseOrder purchaseOrder = new PurchaseOrder();
purchaseOrder.setItemName("name");
BillingAddress billingAddress = new BillingAddress();
billingAddress.setCity("c1"); billingAddress.setStreet("s1"); purchaseOrder.setBillingAddress(billingAddress);
session.save(purchaseOrder);

There's are few question you asked, I tried to go through all of them and answer each one:
What does #AnnotationOverride do?
answer here: What does #AttributeOverride mean?
The second question is a bit unclear to me but I presume you're asking whether you have to include all the fields from the composite key in the PurchaseOrder class.
No, I don't think so. Here's an example I've put together real fast:
#Entity
#Table(name = "PURCHASE_ORDER")
public class PurchaseOrder{
#Id
private BillingAddress billingAddress;
//getters & setters
#Embeddable
public static class BillingAddress implements Serializable {
#Column(name = "street")
private String street;
#Column(name = "city")
private String city;
#Column(name = "itemName")
private String itemName;
//getters & setters
}
}
Don't worry about the syntax, just the structure. You can even add extra field into PurchaseOrder which isn't an id.
Should I use composite keys or not?
answer here: Should I use composite primary keys or not?

Well, your PurchaseOrder class does not extend from a mapped entity of any kind, and neither do the properties that you are (currently) applying the #AttributeOverrides to. So, there is nothing to actually override and your JPA provider is simply ignoring the annotations. What I think you are trying to do is define an embedded id for an entity, while overriding some of the column mappings for that id. You can do this with some modifications to your current code:
#Entity
public class PurchaseOrder {
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name = "street", column = #Column(name = "BILLING_STREET")),
#AttributeOverride(name = "city", column = #Column(name = "BILLING_CITY")) })
private BillingAddress billingAddress;
private String itemName;
// Constructors, Getters/Setters
}
Note that I've changed the names of the overridden attributes, since with your current example, the embedded id name and overridden names are the same.

Related

PersistenceException, Column 'id' specified twice

I have the following files in Play Framework 2.2.3
Controller:
public class Comment extends Controller
{
public Result create(UUID id)
{
models.blog.Blog blog = models.blog.Blog.finder.byId(id);
Result result;
if(blog == null)
{
result = notFound(main.render("404", error404.render()));
}
else
{
Form<models.blog.Comment> commentForm = Form.form(models.blog.Comment.class);
commentForm = commentForm.bindFromRequest();
if(commentForm.hasErrors())
{
result = badRequest(Json.toJson(commentForm));
}
else
{
models.blog.Comment comment = commentForm.get();
comment.setId(UUID.randomUUID());
comment.setTimeCreated(new Date());
comment.setBlogId(blog.getId());
comment.save();
result = ok(Json.toJson(comment));
}
}
return result;
}
}
And two models
#Entity
#Table(name="blog")
public class Blog extends Model
{
private static final SimpleDateFormat MONTH_LITERAL = new SimpleDateFormat("MMMMM"),
DAY_NUMBER = new SimpleDateFormat("d"),
YEAR_NUMBER = new SimpleDateFormat("yyyy");
public static Finder<UUID, Blog> finder = new Finder<UUID, Blog>(UUID.class, Blog.class);
#Id
#Column(name="id",length=36, nullable=false)
public UUID id;
#OneToOne
#JoinColumn(name="author_id")
public User author;
#Column(name="title",length=255)
public String title;
#Column(name="summary",length=255)
public String summary;
#Column(name="url",length=255)
public String url;
#Column(name="content")
public String content;
#Column(name="time_updated")
public Date time_created;
#Column(name="time_created", nullable=false)
public Date time_updated;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name="blog_id")
public List<Comment> comments;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(
name="blog_tag_map",
joinColumns={ #JoinColumn(name="blog_id", referencedColumnName="id") },
inverseJoinColumns={ #JoinColumn(name="tag_id", referencedColumnName="id") }
)
public List<Tag> tags;
public List<Comment> getComments()
{
return this.comments;
}
}
#Entity
#Table(name="blog_comment")
public class Comment extends Model
{
private static final SimpleDateFormat MONTH_LITERAL = new SimpleDateFormat("MMMMM"),
DAY_NUMBER = new SimpleDateFormat("d"),
YEAR_NUMBER = new SimpleDateFormat("yyyy");
#Id
#Column(name="id",length=36, nullable=false)
public UUID id;
#Column(name="blog_id", length=36)
public UUID blog_id;
#ManyToOne
public Blog blog;
#Column(name="content", length=500)
public String content;
#Column(name="website", length=255)
public String website;
#Column(name="name", length=255)
public String name;
#Column(name="time_created", updatable=false)
public Date time_created;
}
I have excluded some setters and getters from these models for brevity, so it doesn't clog up this post.
When I attempt to make a POST request to the aforementioned controller, everything goes fine until I get to the "comment.save()" statement in the controller file, then I get the following error.
I'm unsure why this save isn't going through, and why there is a column conflict.
Help much appreciated
The issue lies in the fact that you have defined basically two foreign key columns for Blog in your Comment's entity:
#Column(name = "blog_id", length = 36)
public UUID blog_id;
#ManyToOne
public Blog blog;
The default column name for your 'blog' field is: blog_id
However, you've already named your 'blog_id' column that.
Interestingly, no error/warning is thrown when creating this table...
So when you call comment.save(), the following insert statement is generated:
insert into blog_comment (id, blog_id, content, website, name, time_created, blog_id) values (?,?,?,?,?,?,?)
Notice a reference to 'blog_id' column twice, which is invalid.
And this is because of the above double mapping.
To fix, just give your 'blog' property a different name to use for the foreign key column:
#Column(name = "blog_id", length = 36)
public UUID blog_id;
#ManyToOne
#JoinColumn(name = "blogId")
public Blog blog;
I'm not sure why you're mapping your entities like this (perhaps legacy schema?) but the 'blog_id' fields seem to be redundant (and confusing) as you already have an entity mapping in the form of your 'blog' property.
This question is pretty old, but for any future reference i have found this answer that solved my problem.
After numerous searchers around the web I found this answer here - thanks to jtal!
Just to summaries the problem:
Using Ebean i have made a #ManyToOne entity that is not implemented in the database in anyway,
even more the join field, in your case
blogId
is a valid field that has values of its own.
when trying to join the column on that field, it will always fail because it creates this sql query:
SELECT
*
FROM
blog_comment;
select
t0.id c0,
t0.blog_id c1,
t0.content c2,
t0.website c3,
t0.time_created c4,
t0.blog_id c5 <---- notice this duplicate
from
blog_comment t0
in order to solve this, i tell ebean not to use the second set of properties.
your new ebean element should look something like this:
#ManyToOne
#JoinColumn(name = "blogId", insertable = false, updatable = false)
public Blog blog;
hope this helps! =)

org.hibernate.ObjectNotFoundException issue with using list()

The following query throws the exception:
Query query = session.createQuery("from Associate as a order by a.username asc");
associates = query.list();
org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [ca.mypkg.model.Associate#0]
If I create an entry in the database with id of 0 it works just fine. I don't really get it because I'm just trying to load all the entries in the db not just a specific one.
Similar questions I've found have been concerned with trying to load an object with a given ID I'm doing no such thing.
Associate class:
#Table(name = "user")
#XmlRootElement(name = "associate")
public class Associate implements Serializable {
private String username;
private String password;
private String firstName;
private String lastName;
private String userType;
private int id;
private String email;
private String isActive;
private Department dept;
private String lastUpdated;
private String associate_type;
// ...
#Id
#GeneratedValue
public int getId() {
return id;
}
#OneToOne
#JoinColumn(name = "dept")
public Department getDept() {
return dept;
}
From my experience this type of error message usually means it does not find joined entity by mentioned id, and not the entity requested in the query (Associate, in your case).
My guess is that Associate class contains a join entity which has primitive type primary key.

OneToOne mapping to an Entity from multiple owning entities

I am a JPA newbie. I have what I believe is a very simple example, but I am having trouble implementing it. Basically, I have an entity that I want to "reuse" in other entities.
I have a User having two fields - Home and Office. Each of these in turn refers to an Address, like so:
#Entity
public class User extends Model {
#Column
private String name;
#OneToOne
private Home home;
#OneToOne
private Office office;
}
#Entity
public class Home extends Model {
#OneToOne
private Address address;
#OneToOne(mappedBy="home")
private User user;
// ...
}
#Entity
public class Office extends Model {
#OneToOne
private Address address;
#OneToOne(mappedBy = "office")
private User user;
// ...
}
#Entity
public class Address extends Model {
#Column
private String line1;
#Column
private String line2;
#Column
private String city;
/*
* Is this possible? Being owned by two entities?
*/
#OneToOne(mappedBy="address")
private Home home;
#OneToOne(mappedBy="address")
private Office office;
// ...
}
How do I achieve this?
EDIT
I was originally running into this exception:
org.hibernate.AnnotationException: #OneToOne or #ManyToOne on example.Home.address references an unknown entity: example.Address
It turns out that one of the classes had imported the #Entity annotation from org.hibernate instead of from javax.persistence. After fixing this, the original exception went away (this helped me pin point the issue)
However, I now have a new exception:
org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: example.User.home -> example.Home
I don't quite understand this exactly. Should I call save the Home object before I do so on the User instance?
I fixed the problem by adding cascade=CascadeType.ALL attribute to all the #OnetoOne annotations in all the entities.
Without this annotation, I would have to save each owned entity before saving the owning one. For example, I'd have to save a Home before saving the User that the home belongs to.
Adding the cascade attribute to the annotation takes care of this for you. You only need to save the User and the Home and Addresses are automatically saved for you.
This post helped me solve the problem.
Can you make only 1 class User:
#Entity public class User extends Model
{
#Column
private String name;
#Embedded
#AttributeOverrides({
#AttributeOverride(name = "line1", column = #Column(name="column11")),
#AttributeOverride(name = "line2", column = #Column(name="column21")),
#AttributeOverride(name = "city", column = #Column(name="column31"))
})
private Address homeAddress;
#Embedded
#AttributeOverrides({
#AttributeOverride(name = "line1", column = #Column(name="column12")),
#AttributeOverride(name = "line2", column = #Column(name="column22")),
#AttributeOverride(name = "city", column = #Column(name="column32"))
})
private Address officeAddress;
// .. setters, getters
}
#Embeddable
public class Address extends Model
{
private String line1;
private String line2;
private String city;
// .. getters, setters
}

JPA - Entity design problem

I am developing a Java Desktop Application and using JPA for persistence. I have a problem mentioned below:
I have two entities:
Country
City
Country has the following attribute:
CountryName (PK)
City has the following attribute:
CityName
Now as there can be two cities with same name in two different countries, the primaryKey for City table in the datbase is a composite primary key composed of CityName and CountryName.
Now my question is How to implement the primary key of the City as an Entity in Java
#Entity
public class Country implements Serializable {
private String countryName;
#Id
public String getCountryName() {
return this.countryName;
}
}
#Entity
public class City implements Serializable {
private CityPK cityPK;
private Country country;
#EmbeddedId
public CityPK getCityPK() {
return this.cityPK;
}
}
#Embeddable
public class CityPK implements Serializable {
public String cityName;
public String countryName;
}
Now as we know that the relationship from Country to City is OneToMany and to show this relationship in the above code, I have added a country variable in City class.
But then we have duplicate data(countryName) stored in two places in the City class' object: one in the country object and other in the cityPK object.
But on the other hand, both are necessary:
countryName in cityPK object is necessary because we implement composite primary keys in this way.
countryName in country object is necessary because it is the standard way of showing relashionship between objects.
How to get around this problem?
countryName in CityPK should be marked read-only using #Column(insertable = false, updatable = false) and both countryNames should be mapped to the same column (using name property):
#Entity
public class City implements Serializable {
#EmbeddedId
private CityPK cityPK;
#ManyToOne
#JoinColumn(name = "countryName")
private Country country;
}
#Embeddable
public class CityPK implements Serializable {
public String cityName;
#Column(name = "countryName", insertable = false, updatable = false)
public String countryName;
}
IMO the proper way to deal with such issues would be to use a generated internal (typically Long) ID instead of a natural primary key - this eliminates the whole problem. Of course, this requires a modification of your DB schema, but from your post I assume that this is possible.
#Entity
public class City implements Serializable {
private Long id;
private String name;
private Country country;
#Id
#GeneratedValue
#Column(name = "CITY_ID")
public Long getId() {
return this.id;
}
private void setId(Long id) {
this.id = id;
}
// more getters, setters and annotations
}

How to store some of the entity's values in another table using hibernate?

is there a simple way to persist some of the fields in another class and table using hibernate.
For example, I have a Person class with name, surname, email, address1, address2, city, country fields. I want my classes to be:
public class Person
{
private String name;
private String surname;
private String email;
private Address address;
// ..
}
public class Address
{
private Person person; // to whom this belongs
private String address1;
private String address2;
private String city;
private String country;
// ..
}
and I want to store Address in another table. What is the best way to achieve this?
Edit: I am using annotations. It does not have to be the way I described, I am looking for best practices.
Edit 2: What will be the Id of Address?
PS. If there is a way to make Address immutable (to use as a value object) that is even better, or maybe not because I thought everything from wrong perspective :)
map Address as an entity and add a primary key (an auto-generated id)
map the relation between Person and Address as one-to-one (#OneToOne on each field)
With Hibernate 3.5 it is possible to define foreign generators (aka. JPA mapping), details are here.
It is pretty straight forward Person should implement Serializable then #Id annotation is added to person.
#Entity
#AccessType(value = "field")
#Table(name = "addresses")
public class Address
{
#Id
#OneToOne
#JoinColumn(name = "person_id")
private Person person;
// ...
}
There is an alternative but I really like the first one:
#Entity
#AccessType(value = "field")
#Table(name = "addresses")
public class Address
{
#Id
private int personId;
#MapsId
#OneToOne
#JoinColumn(name = "person_id")
private Person person;
// ...
}

Categories

Resources