Hibernate: insert ManyToMany, generate ID in relationship table - java

Let I have Tables (Books and Authors have ManyToMany relation ship)
BOOKS
id
book_name
AUTHORS
id
author_name
BOOKS_AUTHORS
id
book_id
author_id
I map this Tables on Entities
class Books
{
#Id
long id;
#Column(name = "author_name")
String name;
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(name = "BOOKS_AUTHORS",
joinColumns = #JoinColumn(name = "book_id"),
inverseJoinColumns = #JoinColumn(name = "author_id"))
List<Authots> authors;
// setter, getters, adder
}
class Authors
{
#Id
long id;
#Column(name = "author_name")
String name;
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(name = "BOOKS_AUTHORS",
joinColumns = #JoinColumn(name = "author_id"),
inverseJoinColumns = #JoinColumn(name = "book_id"))
List<Books> books;
// setter, getters, adder
}
Now, I try do next
public void addAuthor(final String bookName, final Author author)
{
final Book book = // get book from database by bookName
if (book == null)
throw new DataNotFoundException("Book not found :(");
author.addBook(book);
book.addAuthor(author);
entityManager.persist(author);
}
1) I get exception that ID of BOOKS_AUTHORS record must be not NULL
2) How I can Generate ID (ising sequance generator) for relationship-table BOOKS_AUTHORS

You cannot use #ManyToMany to create join table with id field in it.
If id field in the join table is a requirement, you have to create a special entity class (such as BookAuthor) for that table and connect it with books and authors with #ManyToOne/#OneToMany relationships.
Otherwise you need to remove id field from BOOKS_AUTHORS table.

#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
If books_authors is not mapped using Hibernate, then use the database side auto id incrementor. Example:
ALTER table book_authors CHANGE id long NOT NULL AUTO_INCREMENT;

Try using Projection
session.CreateCriteria(typeof (BOOKS_AUTHORS))
.SetProjection(Projections.Max("Id"))
.UniqueResult()
and add to it +1 :) Sory its .net code im not familiar with java hope it will help you.

In SpringBoot 1.5.3 and spring-boot-starter-data-jpa you can use annotation {#OrderColumn(name = "id")}. For you example it should looks like:
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(name = "BOOKS_AUTHORS",
joinColumns = #JoinColumn(name = "author_id"),
inverseJoinColumns = #JoinColumn(name = "book_id"))
#OrderColumn(name = "id")
List<Books> books;

Related

How to do One to One association in hibernate with foreign key and join table and unidirectional

I wish to have a one to one association with a join table in unidirectional way. -
Tables :
A (A_id, D_id, A_Data)
B (A_id, C_id) // Join table just contain relation between A and C
C (C_id, C_Data)
Class A {
.
.
#OneToOne(cascade = CascadeType.ALL)
#JoinTable(name = "B",
joinColumns = #JoinColumn(name = "A_id", referencedColumnName = "A_id"),
inverseJoinColumns = #JoinColumn(name = "C_id", referencedColumnName = "C_id"))
private C c;
}
I am using hibernate with jpa 2.0.
Entity D is not important in the model hence ignored.
I only wish to read data ,hence insert/update/delete use cases should not be concern, but one can suggest best practice in that case also.
This setup does not work. Can some one suggest how to do it in correct way?
It gives following exception
org.hibernate.MappingException: Unable to find column with logical name: A_id in org.hibernate.mapping.Table(A) and its related supertables and secondary tables
In order to get your desired schema:
// Given the following C entity
#Entity
public class C {
#Id
#Column(name = "C_ID")
private long id;
private String C_Data;
//...
}
// A Entity should be
#Entity
public class A {
#Id
#Column(name = "A_ID")
private long id;
private String A_Data;
#OneToOne(cascade = CascadeType.ALL )
#JoinTable(name = "B", joinColumns = #JoinColumn(name = "A_id"), inverseJoinColumns = #JoinColumn(name = "C_id", unique = true))
private C c;
//...
}
I've omitted referencedColumnName, so hibernate will map it to the entity primary key.
Note also that A_id column of B table will be the primary key.

Hibernate querying on ghost column

I have two entities that used to be linked by a one to many relation but now they are linked by a many to many relation declared as follow :
SalesTeam entity :
#Entity
#Table(name = "SALES_TEAMS")
public class SalesTeam {
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.REFRESH, CascadeType.PERSIST})
#JoinTable(name = "WORKFLOW_FOR_SALESTEAM", inverseJoinColumns = {
#JoinColumn(name = "WFC_ID")
})
private List<WorkFlowCode> workFlowCodes = new ArrayList<>();
}
And the WorkFlowCode entity :
#Entity
#Table(name = "WORK_FLOW_CODE")
public class WorkFlowCode {
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.REFRESH, CascadeType.PERSIST})
#JoinTable(name = "WORKFLOW_FOR_SALESTEAM", inverseJoinColumns = {
#JoinColumn(name = "ST_ID")
})
private List<SalesTeam> salesteam = new ArrayList<>();
}
As I said the relation use to be one SalesTeam for several workflow codes but the requirement change and now it need to be a many to many relation. So I had a relation table and remove the former SALES_TEAM_ID column from the WORK_FLOW_CODE table. The problem is that now I always get an error when I try to get the WorkFlowCode from a SalesTeam. It appears that hibernate still adds the removed column to the query thus the relation had changed and nothing is left from the former relation description.
Here is the hibernate generated query :
select workflowco0_.SALES_TEAMS_ID as SALES_TE3_13_0_, workflowco0_.WFC_ID as WFC_ID4_16_0_, workflowco1_.ID as ID1_17_1_ from WORKFLOW_FOR_SALESTEAM workflowco0_ inner join WORK_FLOW_CODE workflowco1_ on workflowco0_.WFC_ID=workflowco1_.ID where workflowco0_.SALES_TEAMS_ID=?
As you can see the former SALES_TEAM_ID from WORK_FLOW_CODE table is still there.
How can I remove it ?
Thx

Why is #ManyToMany not working with non-primary key columns?

I have 2 entities - User and Role which have following relations: User has a manytomany relation to itself and a manytomany relation with the Role entity.
#Entity
public class UserEntity implements Serializable {
#Id
#Column(length = 12, columnDefinition = "BINARY(12)", name = "Id", unique = true)
private byte[] id;
#Column(name = "Login", unique = true, nullable = false)
private String login;
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "User_Role",
joinColumns = { #JoinColumn(name = "UserLogin", referencedColumnName = "Login") },
inverseJoinColumns = { #JoinColumn(name = "RoleId", referencedColumnName = "Id") })
private Set<RoleEntity> roles;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinTable(name = "User_User",
joinColumns = { #JoinColumn(name = "UserParent") },
inverseJoinColumns = { #JoinColumn(name = "UserChild") })
private Collection<UserEntity> children;
...
}
and Role:
public class RoleEntity implements Serializable{
#Id
#Column(name = "Id", unique = true, nullable = false)
private String id;
...
}
The strange thing about the setup of DB is that the User_User relation is based on the binary Id keys
create table if not exists User_User (
UserParent binary,
UserChild binary
);
and the user-role is based on varchars
create table if not exists KNUser_UserRole (
UserLogin varchar,
RoleId varchar,
);
Now, when it runs, the user-user relationship work well. However, when I try to access the collection returned for roles, I get a ClassCastException:
java.lang.ClassCastException: **.entity.UserEntity cannot be cast to [B
at org.hibernate.type.descriptor.java.PrimitiveByteArrayTypeDescriptor.extractHashCode(PrimitiveByteArrayTypeDescriptor.java:41)
at org.hibernate.type.AbstractStandardBasicType.getHashCode(AbstractStandardBasicType.java:201)
at org.hibernate.type.AbstractStandardBasicType.getHashCode(AbstractStandardBasicType.java:205)
at org.hibernate.engine.spi.EntityKey.generateHashCode(EntityKey.java:114)
at org.hibernate.engine.spi.EntityKey.<init>(EntityKey.java:79)
at org.hibernate.internal.AbstractSessionImpl.generateEntityKey(AbstractSessionImpl.java:240)
at org.hibernate.engine.internal.StatefulPersistenceContext.getCollectionOwner(StatefulPersistenceContext.java:740)
at org.hibernate.loader.Loader.readCollectionElement(Loader.java:1181)
at org.hibernate.loader.Loader.readCollectionElements(Loader.java:800)
at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:651)
at org.hibernate.loader.Loader.doQuery(Loader.java:856)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:289)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:259)
at org.hibernate.loader.Loader.loadCollection(Loader.java:2175)
at org.hibernate.loader.collection.CollectionLoader.initialize(CollectionLoader.java:61)
at org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:622)
at org.hibernate.event.internal.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:82)
at org.hibernate.internal.SessionImpl.initializeCollection(SessionImpl.java:1606)
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:379)
at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:112)
at org.hibernate.collection.internal.PersistentSet.iterator(PersistentSet.java:180)
It looks like the UserEntity is being cast to some binary(?) thing. However, the first relation between users themselves works fine, but the one with another table is wrong.
I am using different columns of different types to join tables. Is it allowed to do it this way?
Another strange thing is that when I switch the #Id annotation to be on the login field, the roles work fine, no issue, but then of course the self-join PersistentBag key is the Login instead of Id, which breaks the relation and no results are retrieved. But the conversion from UserEntity to the "[B" is not done.
Also if I leave things as in example and change the Id type to String (and the DB to varchar) it also starts working (of course not consistently with the User_User table).
What am I doing wrong? What is the reason for getting the classcastexception in this case? Why it work when I change the byte[] to String? Please let me know if you have any ideas. I do not want to change the DB design cause this would lead to lots migration and compatibility issues for clients already using the DB.
Just a note: the #Id has to be on the Id binary field as otherwise I wouldn't be able to make a self-join (I was unable to point twice to a column not being a primary key see: Is Hibernate ManyToMany self-join possible for non-key columns? getting mappingException).
Cheers
Adam
the referred column in your join table must be unique entry, here if you put #Id on login field then it works fine,but when you change it to different other than #Id column you cant be sure about the entries will be unique.what you can do is,
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "User_Role",
joinColumns = { #JoinColumn(name = "UserLogin", referencedColumnName = "Id") },
inverseJoinColumns = { #JoinColumn(name = "RoleId", referencedColumnName = "Id") })
private Set<RoleEntity> roles;
I think it should work.

JPA #SecondaryTable foreign key violation

I want to map two entities in a one to many fashion.
A->[B, B]
I want to add to the join table more fields. Pojos looks like:
#Entity
#Table(name = "A", schema = "examples")
#SecondaryTable(name = "A_B", pkJoinColumns = #PrimaryKeyJoinColumn(name = "a_id", referencedColumnName = "id"))
public class A
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Basic
private String name;
#Basic
private Integer field1;
#Column(table = "A_B", name = "field2")
private Integer field2;
#OneToMany(cascade = {CascadeType.ALL})
#JoinTable(name = "A_B", joinColumns = {#JoinColumn(name = "a_id")}, inverseJoinColumns = {#JoinColumn(name = "b_id")})
private List<B> datastores;
}
#Entity
#Table(name = "B", schema = "examples")
#SecondaryTable(name = "A_B", pkJoinColumns = #PrimaryKeyJoinColumn(name = "b_id", referencedColumnName = "id"))
public class B
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Basic
private String field1;
#Basic
private int field2;
#Column(table = "A_B", name = "field3")
private int field3;
}
Thing is that in order to add I had to remove the foreign key on A_B table. How do I solve the mapping to allow the foreign keys ?
Thanks.
I am missing something, but I don't see why both Entity A and Entity B are mapping to table "A_B". By adding it to Entity A as a secondary table, you are stating that every time an insert to Table a occurs, an insert to table A_B must also occur - creating a strict 1:1 relation between rows in the two tables. Except that you do the same thing to entity B, so you will end up with rows in A_B with A_id=somevalue, and B_id= null and others with a_id=null while b_id=somevalue. Table "A_B" looks like a relation table, so this probably isn't what you want.
If A_B is a relationtable you should map it using a ManyToMany as you have for the "A_B" table. If there are extra fields that need to be populated, create a AB Entity, and create a OneToMany from A->AB and B->AB, and ManyToOne from AB->A and AB->B.

JPA many to one/one to many query

I would like to create JPA query based on this tables
**category**
(pk)CategoryID int (10)
category VARCHAR (45)
**templatecat**
(pk/fk)templateId int(10)
(pk/fk)categoryId int (10)
**template**
(pk)templateId int (10)
template madiumtext
I also have a "tempaltecat" table that holds foreign keys for category and template table.I`d like to create query that finds all templates in template table based on category and vice versa.
Here is my table mapping
#Entity
#Table(name = "category")
#OneToMany(cascade = CascadeType.ALL)
#JoinTable(name = "templatecat", joinColumns = { #JoinColumn(name = "categoryId", unique = true) }, inverseJoinColumns = { #JoinColumn(name = "templateId") })
private Set<Template> template;
#Entity
#Table(name = "template")
#ManyToOne(optional = true)
#JoinTable(name = "templatecat", joinColumns = { #JoinColumn(name = "templateId") }, inverseJoinColumns = { #JoinColumn(name = "categoryId") })
private Category category;
Thanks in advance
It looks like a #ManyToMany relationship,
Instead of using #OneToMany and #ManyToMany, you can use the following configuration:
In Category class:
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "templatecat", joinColumns = { #JoinColumn(name = "categoryId", unique = true) }, inverseJoinColumns = { #JoinColumn(name = "templateId") })
private Set<Template> templates;
In Template class:
#Entity
#Table(name = "template")
#ManyMany(optional = true, mappedBy="templates");
private Set<Category> categories;
If you want to see all Templates of a given Category, the query would be:
select o.templates from Category o where o.id = ?
The reverse works as well (all Categories from a Template)
select o.categories from Template o where o.id = ?
Hope it has helped you.

Categories

Resources