Hibernate OneToMany with JoinTable ID generation - java

Could somebody help me in setting appropriate annotation in hibernate for following case:
I have three tables:
Account, Card and AccountCard.
AccountCard is joining table for OneToMany relationship between Card and Account (account has many cards, card is attached to only one account).
I need to add to Account a List cards property and to Card model Account account property. This is the easy thing.
The problem is that I get "Cannot insert null value to AccountCard.id" while persisting Account with Cards.
Also I need to use sequence to generate IDs for joining table but don't know how.
Any help would be very appreciated.
Here is the code in Card:
#ManyToOne(fetch = FetchType.LAZY)
#JoinTable(name = "account_card", joinColumns = #JoinColumn(name = "crd_id"), inverseJoinColumns = #JoinColumn(name = "acc_id"))
private Account account;
I don't want to have a mapping in Account class so List cards is not added.

In your #JoinTable annotation, I see reference to an account_name table and not AccountCard. Is there actually an AccountCard table somewhere?
A join table usually doesn't need an id key of its own, and if you have hibernate autogenerate your table DDL it won't include one.
If you do indeed need an id on the join table, I don't think there's a way or a need to make hibernate aware of it, but you should make the column NOT NULL AUTO_INCREMENT in your SQL DDL.

Did you try to generate the tables first in the database (in my case mysql) and then create the entity with an ide like nebans? An auto increment id column in mysql then ends with:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "ID")
private Integer id;
Check out http://netbeans.org/kb/docs/javaee/ecommerce/entity-session.html for a sample how to use netbeans to create entities from database

Related

Duplicate Records using Bidrectional Mapping in Hibernate

I want to store workers in an Organization object, For this, I have an Organization class having One to Many Mapping for workers.
#OneToMany(cascade=CascadeType.ALL,fetch = FetchType.EAGER)
#JoinTable(name = "ORGANIZATION_WORKER", joinColumns = { #JoinColumn(name = "orgIdPK") }, inverseJoinColumns = { #JoinColumn(name = "id") })
public Set<Worker> workerList;
"ORGANIZATION_WORKER is a temporary table that holds the mapping"
Then I have a Worker class where I have Many to One Mapping as follow.
#ManyToOne(cascade=CascadeType.ALL,fetch = FetchType.LAZY)
#JoinColumn(name = "orgIdPK")
public Organization organization;
Now when I save the organization after inserting workers into it using hibernate session, i-e
session.saveOrUpdate(org);
insert query runs twice (N+1 issue). `
Hibernate: insert into ORGANIZATION_WORKER (orgIdPK, id) values (?, ?)
Hibernate: insert into ORGANIZATION_WORKER (orgIdPK, id) values (?, ?)
So when I expose Organization data on rest API, all records are shown twice because of which pagination gets wrong on the rest API. Anyone knows how to solve this issue?
The problem is that you are creating a circular reference. You are inserting a organization which has workers in it, and those workers have that same organization as a parameter, so it is inserting twice.
To solve this issue, Hibernate has some keywords applicable to columns: "insertable" and "updatable", that specify whether or not the value of that column will be inserted or updated in DB.
In general, the proper thing to do is to make the OneToMany relationship non-insertable and non-updatable; if you want to change the Organization of a Worker, you will probably do it from that Worker; the class responsible for updating that value is the Worker itself, that is, the class with the ManyToOne association.
So you need to do this:
#OneToMany(cascade=CascadeType.ALL,fetch = FetchType.EAGER, insertable=false, updatable=false)
This way all changes in this column will be ignored when the entity inserts or updates in DB.
Remove the annotations from Worker.
#ManyToOne(cascade=CascadeType.ALL,fetch = FetchType.LAZY)
#JoinColumn(name = "orgIdPK")
http://docs.jboss.org/hibernate/orm/5.3/userguide/html_single/Hibernate_User_Guide.html#collections-map-unidirectional

mapping two fields from one table on to other

I have these 2 tables
Users(
id PK,
name VARCHAR(30)
);
The other table is
Orders(
id PK,
orderBy FK Users.id,
orderTo FK Users.id
);
Now, what I want to do is to create Orders entity class which maps orderBy and orderTo to the user. But the most thing i am confuse about is what cascading i should use.
class Orders{
///
#ManyToOne(fetch = FetchType.Lazy
#JoinColumn(name="orderBy")
Users orderBy;
///
#ManyToOne(fetch = FetchType.Lazy
#JoinColumn(name="orderTo")
Users orderTo;
}
I am thinking to create two fields in Users Table such that
class Account{
///
#OneToMany(fetch = FetchType.Lazy)
#JoinColumn(name="orderTo")
List<Orders> ordersReceived;
///
#OneToMany(fetch = FetchType.Lazy)
#JoinColumn(name="orderBo")
List<Orders> ordersPlaced;
}
But again, I am not sure what cascading shall i use. My Users table will be populated by some other processes so orders has nothing to do with. I don't want when i am placing an order, that particular transaction should add/delete anything. HOWEVER, i might need to update a specific field of User whenever i place an order.
I'll suggest to avoid to use cascade at all (if possible)... When you place an order, you should follow the following steps:
1) load your user from your database
2) create your order ...
3) linkup your order to your user (this is, order.setOrderBy(user))
4) persist your order with your EntityManager.
5) Change your user attribute.
From my experience, Cascade should be used carefully. I only used it for persist entities in one shoot (Cascade.PERSIST) (example: persisting a newly user with another new entities like orders)

JPA/Hibernate: Map many-to-many relationship when join table has own primary key

I have three tables with simple structure:
pub [id, name]
days [id, name]
pub_days [id, pub_id, days_id]
For some unholy reason, somebody thought that compound identity for pub_days table (that would be pub_id + days_id) is not enough and added own primary key. I can't change it now, other and larger system depends on that. #sigh
I am trying to map this to Hibernate with standard #ManyToMany JPA annotation like so (I omitted getters, setters, #Entitiy annotations and other clutter):
class Pub {
#ManyToMany(cascade = {CascadeType.ALL})
#JoinTable(name = "pub_days",
joinColumns = {#JoinColumn(name = "pub_id")},
inverseJoinColumns = {#JoinColumn(name = "days_id")})
#OrderBy("id")
private List<Day> pubOpeningDays;
}
class Day {
#Id Long id;
String name.
}
when I execute following code:
Day day = repository.find(Day.class, id);
pub.getPubOpeningDays().add(day);
repository.persist(pub);
I get this error:
ERROR: ORA-01400: cannot insert NULL into ("PUB"."pub_days"."id")
Sadly, that makes perfect sense, because I haven't mapped that ID anywhere. The thing is, I don't even want to. I want it to be generated, but not sure how do I overcome this issue with #ManyToMany mapping. Any ideas?
What you can do is like I mentioned in my comments you can create a separate entity CD which will in turn connect with two classes A and B, Now relationship would be many to many between A and B, and hence A (many to many) CD (many to many) B. Now as per your requirement whenever you need to fetch the instance of A or B, what you can do is simply fire a query in the DB with proper parameters i.e id of a or id of b this will help you get your required result.
I only see two choices, either you change your mapping to a list of PubDay as samwise-gamgee told you in the comments or you add a trigger on insert on table pub_days which set a value for the column id if it is null (it could be using a sequence). But this approach depends on the features supported by your DB.

Storing data using hibernate for inter related tables

I have two tables (say table A and table B). Table B has foreign key from table A primary key. I generated my java entities using netbeans IDE and i now have something like:
For table A:
#Entity
#Table(name = "WORKFLOW_TRANSACTION")
public class WorkflowTransaction implements {
#OneToMany(mappedBy = "wtId")
private Collection<WorkflowTask> workflowTaskCollection;
#Id
#Basic(optional = false)
#NotNull
#Column(name = "APP_ID")
private BigDecimal appId;
.
.
.
For table B:
#Entity
#Table(name = "WORKFLOW_TASK")
public class WorkflowTask implements Serializable {
#JoinColumn(name = "WT_ID", referencedColumnName = "APP_ID")
#ManyToOne
private WorkflowTransaction wtId;
#Id
#Basic(optional = false)
#NotNull
#Column(name = "TASK_ID")
private BigDecimal taskId;
#Column(name = "STEP_NUM")
private BigInteger stepNum;
.
.
.
Now my questions are:
What is the correct way to save data, should I create object for table B entity and set it in table A entity and then save table A?
I am generating entity beans using netbeans IDE feature. Are there any known disadvantages of it?, if yes, what?
You have bi-directional association, so you need to setTable B property in Table A entity class and also vice-versa. Since you have not declared cascading, you need to save the Table A entity first and then Table B entity.
Alternatively, if you save Table B and then Table A entity classes, the hibernate generates an extra SQL update command to maintain the relationship.
But if you want hibernate to save Table B entity when you save Table A entity then you need to add Cascade property:
#OneToMany(mappedBy = "wtId", cascade=CascadeType.ALL)
I don't think you will have any disadvantages if you use Netbeans for generating the entity classes, it save you time in writing the entities. But if you want to learn then writing entities without Netbeans is good.
To achieve this you can use two type of techniques XML mapping or Annotations
In both these techniques the common point is use
cascade="save-update"
What happens is that you set an attribute in one table getter and it will automatically insert into the many relation when inserting into one relation table

How to do bulk delete in JPA when using Element Collections?

I am having trouble working out how to do a bulk delete of a Person object using JPA, when the Person objects contain data stored using an #ElementCollection. Any ideas on how to do this would be much appreciated.
#Entity
#Table(name="at_person")
public class Person implements Comparable<Person> {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="id")
private long id = 0;
#Column(name="name", nullable = true, length = 128)
private String name = "";
#ElementCollection
#Column(name = "email")
#CollectionTable(name = "person_email", joinColumns = #JoinColumn(name = "person_id"))
private Set<String> email = new HashSet<String>();
}
What I am doing at the moment is this, and it fails with a foreign key constraint error:
Query query=em.createQuery("DELETE FROM Person");
Caused by: java.sql.SQLException: integrity constraint violation:
foreign key no action; FKCEC6E942485388AB table: PERSON_EMAIL
If it can be a pure JPA annotation rather than a Hibernate annotation that would be a bonus!
I'll let you interpret the part of the JPA 2.0 specification that mentions that a bulk delete operation is not cascaded:
4.10 Bulk Update and Delete Operations
...
A delete operation only applies to
entities of the specified class and
its subclasses. It does not cascade to
related entities.
And the fact is that Hibernate won't cascade a delete to a collection table either. This has been reported in HHH-5529 and the suggested approaches are:
You could also (a) clean up the collection table yourself or (b) use cascading foreign keys in the schema.
In other words, (a) use native SQL or (b) use a cascade delete constraint at the database level - and you'll have to add it manually, I don't think you can use #OnDelete with the #ElementCollection annotation (same story as HHH-4301 IMO).

Categories

Resources