Persisting OneToMany relationship Using JPA without Join table - database

I have the following tableS:
`notification_job_execution`
(`jobId`,
`createdDate`,
`messageBody`)
`notification_task_execution`
(`taskId`,
`jobId`,
`status`,
`createdDate`)
Havin oneToMany relationshop (notification_job_execution ,notification_task_execution)
(1..n)
I have the following entities
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
private String jobId;
#Temporal(TemporalType.DATE)
private Date createdDate;
private String messageBody;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "notificationJobEntity", cascade=CascadeType.ALL)
private Set<NotificationTaskEntity> notificationTaskEntities = new HashSet<NotificationTaskEntity>(
0);
and:
#Entity
#Table(name = "notification_task_execution")
public class NotificationTaskEntity implements Serializable{
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
private long taskId;
private int jobId;
private String status;
#Temporal(TemporalType.DATE)
private Date createdDate;
#ManyToOne(optional = false)
private NotificationJobEntity notificationJobEntity;
I am using Spring and JPA in order to persist this way:
NotificationJobEntity notificationJobEntity=new NotificationJobEntity();
notificationJobEntity.setCreatedDate(new Date());
notificationJobEntity.setMessageBody("hello youu");
NotificationTaskEntity notificationTaskEntity=new NotificationTaskEntity();
notificationTaskEntity.setCreatedDate(new Date());
notificationTaskEntity.setStatus("success");
notificationTaskEntity.setNotificationJobEntity(notificationJobEntity);
notificationTaskEntity.setNotificationJobEntity(notificationJobEntity);
notificationJobEntity.getNotificationTaskEntities().add(notificationTaskEntity);
notificationDao.save(notificationJobEntity);
I cant see in the database the child record persisted ( which is notificationTaskEntity).
How could I persist the parent and under the hood having the notificationTaskEntity to be persisted to the database as well?

I think you may just need to add the #JoinColumn annotation to indicate which field in the database table represents the ID of the NotificationTaskEntity.
#ManyToOne(optional = false)
#JoinColumn(name = "jobid")
private NotificationJobEntity notificationJobEntity;

You must specify the CascadeType in the NotificationTaskEntity
#ManyToOne(optional = false,CascadeType.PERSIST)
private NotificationJobEntity notificationJobEntity;

Related

Why Hibernate doesnt create a join query

I have ValidationErrorAsset table which have 7 foreign key columns referenced to ValidationErrorMaster.
I wrote Entity classes for them assuming Hibernate will fetch data using some join (less concerned inner/outer for now).
But Hibernate actually writes 8 simple select queries. 1 for ValidationErrorAsset and other 7 for ValidationErrorMaster, gets data and creates the object.
This involves total 8 network calls which is so costly.
Please help me fixing this.
Below are 2 Entities and Hibernate generated queries:
ValidationErrorAsset .class
#Entity
public class ValidationErrorAsset {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer validationErrorAssetId;
#ManyToOne
#JoinColumn(name = "asset_type_id")
private ValidationErrorMaster assetTypeError;
#ManyToOne
#JoinColumn(name = "asset_group_id")
private ValidationErrorMaster assetGroupError;
#ManyToOne
#JoinColumn(name = "name")
private ValidationErrorMaster nameError;
#ManyToOne
#JoinColumn(name = "asset_family_type_id")
private ValidationErrorMaster assetFamilyTypeError;
#ManyToOne
#JoinColumn(name = "source_repository_id")
private ValidationErrorMaster sourceRepositoryError;
#ManyToOne
#JoinColumn(name = "source_repository_path")
private ValidationErrorMaster sourceRepositoryPathError;
#ManyToOne
#JoinColumn(name = "asset_publisher_type_id")
private ValidationErrorMaster assetPublisherTypeError;
}
ValidationErrorMaster.class
#Entity
public class ValidationErrorMaster {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer validationErrorMasterId;
private String errorCode;
private String description;
private Boolean activeInd;
private String updtUsername;
private Integer updtCnt=0;
private LocalDateTime updtDtTm=LocalDateTime.now(ZoneOffset.UTC);;
private String severityLevel;
private Boolean skippable;
}
Hibernate generated MS-SQL query:
Hibernate:
select
validation0_.validation_error_asset_id as validati1_35_,
validation0_.active_ind as active_i2_35_,
validation0_.error_obj as error_ob3_35_,
validation0_.updt_cnt as updt_cnt4_35_,
validation0_.updt_dt_tm as updt_dt_5_35_,
validation0_.updt_username as updt_use6_35_,
validation0_.asset_family_type_id as asset_fa7_35_,
validation0_.asset_group_id as asset_gr8_35_,
validation0_.asset_id as asset_id9_35_,
validation0_.asset_publisher_type_id as asset_p10_35_,
validation0_.asset_type_id as asset_t11_35_,
validation0_.name as name12_35_,
validation0_.source_repository_id as source_13_35_,
validation0_.source_repository_path as source_14_35_
from
validation_error_asset validation0_
Hibernate:
select
validation0_.validation_error_master_id as validati1_36_0_,
validation0_.active_ind as active_i2_36_0_,
validation0_.description as descript3_36_0_,
validation0_.error_code as error_co4_36_0_,
validation0_.severity_level as severity5_36_0_,
validation0_.skippable as skippabl6_36_0_,
validation0_.updt_cnt as updt_cnt7_36_0_,
validation0_.updt_dt_tm as updt_dt_8_36_0_,
validation0_.updt_username as updt_use9_36_0_
from
validation_error_master validation0_
where
validation0_.validation_error_master_id=?
Hibernate:
select
validation0_.validation_error_master_id as validati1_36_0_,
validation0_.active_ind as active_i2_36_0_,
validation0_.description as descript3_36_0_,
validation0_.error_code as error_co4_36_0_,
validation0_.severity_level as severity5_36_0_,
validation0_.skippable as skippabl6_36_0_,
validation0_.updt_cnt as updt_cnt7_36_0_,
validation0_.updt_dt_tm as updt_dt_8_36_0_,
validation0_.updt_username as updt_use9_36_0_
from
validation_error_master validation0_
where
validation0_.validation_error_master_id=?
Rest 4 I skipped to add.

How to update tables with many-to-many relationship when performing crud operations in Spring Boot

I'm trying to create a Spring Boot backend for my project. In the database I have Deck and Word tables with a many-to-many relationship connected via DeckWord table. The bridge table has additional fields and a composite PK consisting of the other 2 tables' PK's.
I am not sure about how I should structure the crud operations in my project. Say I'm trying to add a new word and it should be assigned to a certain deck. What model's controller should handle the post operation in that scenario: Word or DeckWord? Should the Deck's List<DeckWord> be updated as well?
UPDATE:
Included the models, omitted the getters, setters and constructors
#Entity
#Table(name = "deck")
public class Deck {
#Id
#SequenceGenerator(
name = "deck_sequence",
sequenceName = "deck_sequence",
allocationSize = 1
)
#GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "deck_sequence"
)
#Column(name = "deck_id")
private Long id;
#Transient
private Boolean learnt;
private String name;
#OneToMany(mappedBy = "deck", cascade = CascadeType.ALL)
private List<DeckWord> deckwords;
#ManyToOne
#JoinColumn(name="appuser_id",referencedColumnName="appuser_id")
private Appuser appuser;
}
and
#Entity
#Table(name = "word")
public class Word {
#Id
#SequenceGenerator(
name = "word_sequence",
sequenceName = "word_sequence",
allocationSize = 1
)
#GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "word_sequence"
)
#Column(name = "word_id")
private Long id;
private String definition;
private String transcription;
#OneToMany(mappedBy = "word", cascade = CascadeType.ALL)
private List<DeckWord> deckwords;
}
and the bridge table:
#Embeddable
class DeckWordKey implements Serializable {
#Column(name = "deck_id")
Long deckId;
#Column(name = "word_id")
Long wordId;
}
#Entity
#Table
public class DeckWord {
#EmbeddedId
DeckWordKey id;
#ManyToOne
#MapsId("deckId")
#JoinColumn(name = "deck_id",referencedColumnName="deck_id")
Deck deck;
#ManyToOne
#MapsId("wordId")
#JoinColumn(name = "word_id",referencedColumnName="word_id")
Word word;
private Boolean learnt;
private LocalDate last_checked;
private WordGroup wordGroup;
}
Answering your questions:
What model's controller should handle the post operation in that scenario: Word or DeckWord?
Given that a Word should always be assigned to a Deck, then I would use a POST request to the URL "/decks/{deckId}/words" to create a new Word. The request body should include definition and transcription.
Should the Deck's List be updated as well?
Yes, it must. For that, you need to use deckId that you receive as a path parameter.

Spring JPA inserts duplicate select columns when using #MapsId, #AttributeOverride on embedded composite key

I'm developing a Spring Boot REST API application and I've encountered a problem with SQL generation for Transact-SQL (SQL Server) dialect and I'm not sure where I did something wrong.
The application is about storage management and I have two entities: Part and Stock. I've simplified the structure to be as simple as possible.
I have composite PK - PartPK:
#Data #Embeddable
class PartPK {
#Column(name = "PART_ID")
private String partId;
#Column(name = "PART_ORGANIZATION_ID")
private String orgId;
}
... and the entity Part having PartPK as #EmbeddedId:
#Entity #Table(name = "parts")
class Part {
#EmbeddedId
private PartPK id;
}
then I'm having a Stock entity that ties to a Part entity and stores. The entity has a composite PK with the following structure, where I'm overriding attributes from PartPK (giving them STOCK_ prefix)
#Data #Embeddable
class StockPK {
#Column(name = "STOCK_STORE_ID")
private String storeId;
#Embedded
#AttributeOverrides({
#AttributeOverride(name = "partId", column = #Column(name = "STOCK_PART_ID")),
#AttributeOverride(name = "orgId", column = #Column(name = "STOCK_PART_ORGANIZATION_ID")),
})
private PartPK partId;
}
... and enclosing Stock entity where I'm trying to reference the Part entity using #MapsId:
#Entity #Table(name = "stocks")
class Stock {
#EmbeddedId
private StockPK id;
#MapsId("partId")
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "STOCK_PART_ID", referencedColumnName = "PART_ID"),
#JoinColumn(name = "STOCK_PART_ORGANIZATION_ID", referencedColumnName = "PART_ORGANIZATION_ID"),
})
private Part part;
}
Which compiles, but after executing a query from the repository, it generates the following query:
select TOP (?)
stockdb0_.stock_part_organization_id as bis_part0_0_,
stockdb0_.stock_store_id as bis_stor3_0_,
stockdb0_.stock_part_organization_id as bis_part5_0_,
stockdb0_.stock_part_id as bis_part6_0_
from stocks stockdb0_
As you can notice, for some reason it uses 2 times stock_part_organization_id column. The entity has incorrect values after persistence mapping (two Stock rows having the same Store but different parts are considered to be the same entity). When the part attribute is removed from the Stock entity, the query and resulting persistence mapping is correct.
Is there anything I'm doing wrong?
I'm using Spring Boot 2.4.5 (the latest) and Started Data Jpa of the same version.
I think using #IdClass will work better in this case:
class StockPK implements Serializable {
private String storeId;
private Part part;
...
}
#Entity #Table(name = "stocks")
#IdClass(StockPK.class)
class Stock {
#Id
private String id;
#Id
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "STOCK_PART_ID", referencedColumnName = "PART_ID"),
#JoinColumn(name = "STOCK_PART_ORGANIZATION_ID", referencedColumnName = "PART_ORGANIZATION_ID"),
})
private Part part;
...
}
But if you want to use #EmbeddedId:
#Embeddable
public static class StockPK implements Serializable {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "STOCK_PART_ID", referencedColumnName = "PART_ID"),
#JoinColumn(name = "STOCK_PART_ORGANIZATION_ID", referencedColumnName = "PART_ORGANIZATION_ID"),
})
private Part part;
#Column(name = "STOCK_STORE_ID")
private String storeId;
}
#Entity #Table(name = "stocks")
class Stock {
#EmbeddedId
private StockPK id;
// The association is already defined in the key
}
Anyway, you don't have to use #MapsId (that's for something else) and you can find examples of both approaches with more details in the Hibernate ORM documentation.

JPA entity relation in table which sharing same key as foreign and primary?

Now, I have two tables.
1st Table (Object Table):
RefNo (PK) --> auto ascending
Type
Status
...
2nd Table (Object Detail Table):
RefNo (PK) --> FK reference from 1st Table
PolicyNo
DepNo
...
Entity for Object.
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long refnno;
#OneToOne(mappedBy = "obejctTwo")
private ObjectDetail objectDetail;
private String type;
private String status;
Entity for ObjectDetail
#Id
private long refnno;
#OneToOne
#JoinColumn(name = "refnno")
#MapsId
private Object object;
private String policyNo;
private String depNo;
How can I save the Object using jpaRepository for Object which include ObjectDetail inside Object JSON but without knowing the reffno(PK) which is auto generated by db.
{
"objectDetail": {
"policyNo": "12345678",
"depNo": "ABC"
},
"type": "new",
"status": "pending"
}
Would it not be possible to cascade the saving with the annotation #OneToOne(cascade = CascadeType.PERSIST) in the class Object?
I would then create the two objects with the available details, set the relationship and save the class Object.
object.setObjectDetail(objectDetail);
objectdetail.setObject(object);
repository.save(object);
The key of object would be set automatically and objectDetail would get the key from object.
Edit: Entity Example
I was slightly surprised that the code did not work. Therefore, I implemented this for myself. It works perfectly with Spring Boot version 2.3.2.RELEASE and spring-boot-starter-data-jpa.
Your issue might be #OneToOne(mappedBy = "obejctTwo") because this mapping does not exist.
Object
#Entity
public class Object {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long refnno;
#OneToOne(mappedBy = "object",
cascade = CascadeType.ALL,
orphanRemoval = true)
private ObjectDetail objectDetail;
private String type;
private String status;
public setObjectDetail(ObjectDetail objectDetail) {
this.objectDetail = objectDetail;
objectDetail.setObject = this;
}
// Getters and remaining setters...
}
I added a slightly modified setter for objectDetail which helps to keep the bidirectional OneToOne mapping synchronised.
BTW: I changed the datatype of refnno to the object Long. Classes are considered to be better for database entities because than you can test them properly for null. Furthermore, this id can then be used for a JpaRepository<Object, Long>.
ObjectDetail
#Entity
public class ObjectDetail {
#Id
private Long refnno;
#OneToOne
#JoinColumn(name = "refnno")
#MapsId
private Object object;
private String policyNo;
private String depNo;
// Getters and setters...
}
Repository and Execution
I created a simple repository.
public interface ObjectRepository extends JpaRepository<Object, Long> {
}
I then used the save(Object entity) method to persist a new Object with a new ObjectDetail.
Object object = new Object();
object.setType("new");
ObjectDetail objectDetail = new ObjectDetail();
objectDetail.setPolicyNo = "999";
object.setObjectDetail(objectDetail);
objectRepository.save(object);

Join 3 table using Hibernate Entities with multiple columns

Can someone help me how should I join those three tables using JPA?
I already did 2 of 3 entities but please let me know if are ok:
#Entity
public class Pacienti {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String nume;
private String prenume;
//setters & getters
}
#Entity
public class Chestionare {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Id
#Column(name = "id_intrebare")
#GeneratedValue(strategy = GenerationType.AUTO)
private int idIntrebare;
private String intrebare;
//setters & getters
}
As I promise I come back after I'm generating entities automatically. Unfortunately now I have another problem.
Now I have the entity:
#Entity
#Table(name = "pacienti")
#NamedQuery(name = "Pacienti.findAll", query = "SELECT p FROM Pacienti p")
public class Pacienti implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(unique = true, nullable = false)
private int id;
#Column(nullable = false, length = 20)
private String nume;
#Column(nullable = false, length = 20)
private String prenume;
// bi-directional many-to-one association to Consultatii
#OneToMany(mappedBy = "pacienti")
private List<Consultatii> consultatiis;
// bi-directional many-to-one association to DetaliiPacient
#OneToMany(mappedBy = "pacienti")
private List<DetaliiPacient> detaliiPacients;
// bi-directional many-to-one association to Doctori
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "id_doctor", nullable = false)
private Doctori doctori;
// bi-directional many-to-one association to RaspunsChestionar
#OneToMany(mappedBy = "pacienti")
private List<RaspunsChestionar> raspunsChestionars;
public Pacienti() {
}
//setters and getters
}
But when I do :
Query queryResult = sessionFactory.getCurrentSession().createQuery("from Pacienti");
I'm getting:
Pacienti is not mapped [from Pacienti] Error.
Can someone tell me why? I also tried "pacienti is not mapped [from pacienti]" but same result
Thank you!
I would recommend you to use the jpa tools/plugins available with the IDEs which will auto generate these jpa entities for you using the database tables rather than manually creating these.
And they will take care of setting the relationship b/w different entities(db tables) in the auto generation process itself.
If you are Eclipse you can achieve this.
The problem is bcz there is no query with the name "from pacienti" in place of that pass the query name "Pacienti.findAll" in your createQuery method.
Plz let ne know once you try this, if you face any problem

Resources