JPA #OneToMany bidirectional with #JoinTable - Not including all columns in insert query - java

I am trying to persist a OneToMany parent child relation with Join table, but in insert sql for join table only one column is considered (out of two).
insert into ITEM_BIDS_REL (BID_ID) values (?)
It does not include ITEM_ID column and due to that getting below error.
java.sql.SQLIntegrityConstraintViolationException: ORA-01400: cannot insert NULL into ("BIDDING_SYSTEM_OWNER"."ITEM_BIDS_REL"."ITEM_ID")
Two question here:
Why it only includes BID_ID column in INSERT query for ITEM_BIDS_REL join table.
Is the right way to design JOIN TABLE like I have kept BID_ID ad PK,FK and ITEM_ID as FK.
Use case:
Referring the "Online Action (bidding) System" example from Java Persistence with Hibernate book.
An Item can have many Bids on it.
ITEM table
ITEM_ID PK
NAME
INITIAL_AMOUNT
ITEM_BIDS table
BID_ID PK
AMOUNT
ITEM_BIDS_REL Join Table
BID_ID PK (FK from ITEM_BIDS table)
ITEM_ID NOT NULL (FK from ITEM table)
Entity classes:
Item Class
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinTable( name = "ITEM_BIDS_REL",
joinColumns = #JoinColumn(name = "ITEM_ID"),
inverseJoinColumns = #JoinColumn(name = "BID_ID"))
#JsonIgnore
private Set<Bid> bids;
Bid class
#ManyToOne(cascade = CascadeType.ALL)
#JoinTable(name = "ITEM_BIDS_REL",
joinColumns = {#JoinColumn(name = "BID_ID", insertable = false,
updatable = false)},
inverseJoinColumns = {#JoinColumn(name="ITEM_ID", insertable = false,
updatable = false)})
private Item item;
Persistence logic:
public Item addBid(BidDTO bidDTO) {
Item item = itemsRepository.findOne(bidDTO.getItemId());
Bid bid = Bid.builder()
.item(item)
.bidAmount(bidDTO.getBidAmount())
.build();
if(item.getBids() == null){
item.setBids(new HashSet<>());
}
item.getBids().add(bid);
bidRepository.save(bid);
itemsRepository.save(item);
return item;
}

Related

Hibernate: ManyToOne / multiple join columns with one column nullable=true

I hope you can help me on that.
Table A has a multi-column join to Table B where one of the JoinColumns can be nullable...
#Entity
#Table(name = "TABLE_A")
public class TableA {
#ManyToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumns({
#JoinColumn(name = "KEY1_TABLE_A", referencedColumnName = "KEY1_TABLE_B"),
#JoinColumn(name = "KEY2_TABLE_A", referencedColumnName = "KEY2_TABLE_B"),
#JoinColumn(name = "GROUP_TABLE_A", referencedColumnName = "GROUP_TABLE_B", nullable = true)})
private TableB typeB;
}
In TableB object the columns
TABLE_B#KEY1_TABLE_B (not null)
TABLE_B#KEY2_TABLE_B (not null)
TABLE_B#GROUP_TABLE_B (nullable)
are mapped as Strings.
The touple KEY1_TABLE_B /KEY2_TABLE_B /GROUP_TABLE_B is a unique key.
The generated SQL is as follows (shortened)
SELECT
*
FROM
table_a this_
INNER JOIN table_b b_ ON
this_.KEY1_TABLE_A = b_.KEY1_TABLE_B AND
this_.KEY2_TABLE_A = b_.KEY2_TABLE_B AND
this_.GROUP_TABLE_A = b_.GROUP_TABLE_B <-- here is the issue: works only with "is not null" on Oracle
WHERE
this.XYZ=<some-conditions-here>;
if i would write the SQL directly it should be something like
on ... AND (
(this_.GROUP_TABLE_A = b_.GROUP_TABLE_B)
OR (this_.GROUP_TABLE_A is null and b_.GROUP_TABLE_B is null)
)
Thanks for your thoughts and ideas!

Difference between name and referencedColumnName in #JoinColumn annotation?

#ManyToOne
#JoinColumn(name = "someValue" , referencedColumnName = "someOtherValue" )
What values are to be placed in name and referencedColumnName column if 2 tables are linked by ManyToOne association?
Suppose you have Two tables:
1. Department table with columns:
a. Dept_ID (primary key)
b. Dept_Name
2. Employee Table with following column:
a. Emp_Id (primary key)
b. Dept_Id_fk (foreign key)
c. Salary
Now your join column for Employee Entity class will be
#ManyToOne
#JoinColumn(name = "Dept_Id_fk", referencedColumnName = "Dept_ID")
Department department;
So referencedColumnName means column that you are referencing to from your foreign key column.
How would the join column look like for oneToMany association? Here is an example:
Person table:
person_id (pk), person_name
person_reltn table:
person_reltn_id (pk),
child_person_id (fk),
parent_person_id (fk)
For the above tables, if I were to create the Person entity:
class Person(){
#Id
#Column(name = "PERSON_ID")
private long personId;
#NotFound(action = NotFoundAction.IGNORE)
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(
name = "CHILD_PERSON_ID",
referencedColumnName = "PERSON_ID",
updatable = false,
insertable = false)
#Where(clause = "ACTIVE_IND = 1")
#Filter(
name = FilterConstants.END_EFFECTIVE_DATE_TIME_FILTER,
condition = FilterConstants.END_EFFECTIVE_DATE_TIME_CONDITION)
#Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
private final List<PersonRelation> personRelations = new ArrayList<>();
}
In the joinColumn, should the name always have the foreign key (which means the value from entity you are joining to) and the referenceColumnName should alway have the primary key on the entity? If yes, it will be the opposite of Sayantan's response above. Please let me know if I misunderstood the concept.
Update on 03/04/2021
After doing more research, I found the documentation on how to set the referenceColumnName based on the entity mappings[1]. Looks like, for unidirectional OneToMany mapping, the referenced column is in the table of the source entity.
1.https://docs.jboss.org/hibernate/jpa/2.1/api/javax/persistence/JoinColumn.html

Add Primary key on ManyToMany (for Publication)

I have a ManyToMany relation between two entity
#javax.persistence.Entity
#Table(name = "t_aircraft_model")
public class AircraftModel extends DbObject {
#ManyToMany(fetch=FetchType.LAZY)
#JoinTable(name = "t_aircraft_model_entity", joinColumns = { #JoinColumn(name = "aircraft_model_uid", nullable = false) }, inverseJoinColumns = { #JoinColumn(name = "entity_id_LDAP", nullable = false) })
private List<com.airbushelicopter.ahead.db.pojo.Entity> entities ;
But sqlServer doesn't allow me to publish the intermediate table : t_aircraft_model_entity
I thought about 2 solutions
Both column of the table the t_aircraft_model_entity become the primary key (ok in my case a aircraft can't be linked multiple time to the same entity)
I add a 3rd column (id) which will be the primary key
Or ?
But I have no idea how I can do this with hibernate and annotation.
thanks !
First things first. You will need 3 tables to make a many to many relation, of course, you will need to make sure that both of your other tables have a PK
On the code side, you can do like this:
Your Airplace Model:
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
#JoinTable(name = "t_aircraft_entity_relation",joinColumns = {
#JoinColumn(name = "aircraftid", nullable = false, updatable = false)},
inverseJoinColumns = { #JoinColumn(name = "entityid",nullable = false,updatable= false)
})
private Set<com.airbushelicopter.ahead.db.pojo.Entity> entities ;
On your Entity Model:
#ManyToMany(fetch = FetchType.EAGER,mappedBy="entities")
private Set<AircraftModel> aircrafts;
And you will have to create a relation table, like in my example:
CREATE TABLE t_aircraft_entity_relation
(
aircraftid integer NOT NULL,
entityid integer NOT NULL,
CONSTRAINT "PK_AIRCRAFT_ENTITY" PRIMARY KEY (aircraftid, entityid),
CONSTRAINT "FK_AIRCRAFT_ENTITY" FOREIGN KEY (aircraftid)
REFERENCES t_aircraft_model (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT "FK_ENTITY_AIRCRAFT" FOREIGN KEY (entityid)
REFERENCES t_entity_model (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
PS: This piece of SQL is based on Postgresql, so you will have to do a little bit of change.

M to N mapping with hibernate with extra columns using anotation

I need to create a M to N mapping, with anotations, for this tables:
Table Food : Columns: id, description, size, type
Table Ingredients: Columns: id, description, price
Table Food_Ingredients: Columns: food_id (FK), ingredient_id (FK), quantity
Classes:
class Food {
/*mapped fields setter/getters*/
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.DETACH)
#JoinTable( name = "food_ingredient",
joinColumns = { #JoinColumn(name = "food_id", nullable = false) },
inverseJoinColumns = { #JoinColumn(name = "ingredient_id", nullable = false) })
private List<Ingredient> ingredients;
}
class Ingredients {/*mapped fields setter/getters*/}
So, how can I map those entities?
How are you going to represent the quantity in your model? You should create a new entity for FoodIngredients with this property(and links to both other tables) and map it using #OneToMany annotations in Food and Ingredients.
Edit: You can use http://www.mkyong.com/hibernate/hibernate-many-to-many-example-join-table-extra-column-annotation/ for reference

many-to-many where clause

Given 3 tables:
student (id)
student_to_class (student_id, class_id)
class (id)
I'd like to apply a where clause on the student_to_class where student_id = :studentId. I've found many examples that apply where clause on "class" table or "student" table, but not the many-to-many table.
The student table has a #ManyToMany
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(
name = "student_to_class",
joinColumns = { #JoinColumn(name = "student_id", nullable = false) },
inverseJoinColumns = { #JoinColumn(name = "class_id", nullable = false) }
)
private Set<ClassEntity> classes;
The class table has a #ManyToMany
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(
name = "student_to_class",
joinColumns = { #JoinColumn(name = "class_id", nullable = false) },
inverseJoinColumns = { #JoinColumn(name = "student_id", nullable = false) }
)
private Set<StudentEntity> students;
Here is the query I'm trying to translate into Criteria:
select * from student, student_to_class where student_to_class.student_id = 1 and student.id = student_to_class.class_id
I'm trying to figure out how to reference the many-to-many table since I don't have an actual class representing this table.
Criteria c = sessionFactory.getCurrentSession().createCriteria(ClassEntity.class);
c.createAlias("student_to_class", "entities"); // how to reference the student_to_class ?
c.add(Restrictions.eq("entities.user_id", studentEntity.getId()));
But I get an error, which makes sense to me, but I haven't had much luck fixing it:
could not resolve property: student_to_class
Since the studentid in the Student table will be the same as the studentid in the student_to_class table there is no need to filter by the join table. Simply run the where clause against student.student_id

Categories