One to many mapping Spring Jpa - java

I have two classes, Workstation and WorkstationReservation. They are in a one to many relationship, a workstation can have several reservations and a workstation reservation is associated to only one workstation.
With the configuration I have provided I am able to insert a workstation without specifying a workstation reservation (this is the behaviour I want, a workstation initially does not have any workstation reservations on it) but whenever I try to insert a workstation reservation it asks me to provide a workstation, I just want to provide the workstation_name, like this:
{
"workstation_name":"AORUS",
"t_start": "2023-02-08T22:14:00",
"t_end": "2023-02-08T23:30:00",
"t_res": "2023-02-08T22:14:00",
"owner_email": "email#gmail.com",
"cpu_reserved": 20,
"cpu_id": 2,
"ram_reserved":200,
"ram_id":2,
"disk_id":3,
"disk_reserved":300,
"net_reserved":100,
"net_id":1
}
Here are the two java classes:
Workstation.java
#RequiredArgsConstructor
#AllArgsConstructor
#Builder
#Getter
#Entity
public class Workstation {
#Id
private String name;
private String ipv4;
private String operating_system;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "workstation_name")
private List<WorkstationReservation> workstation_reservations;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "workstation_name")
private List<Disk> disks;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "workstation_name")
private List<Network> networks;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "ram_id", referencedColumnName = "id")
private Ram ram;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "cpu_id", referencedColumnName = "id")
private Cpu cpu;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "workstation_name")
private List<Program> programs;
private int max_reservable_hours;
private int min_reservable_hours;
private String location;
private String description;
private String group_name;
}
WorkstationReservation.java
#RequiredArgsConstructor
#AllArgsConstructor
#Builder
#Getter
#Entity
public class WorkstationReservation {
#Id
#SequenceGenerator(name = "workstation_reservation_id_gen",
sequenceName = "workstation_reservation_id_seq",
allocationSize = 1
)
#GeneratedValue(
strategy = GenerationType.SEQUENCE,
generator = "workstation_reservation_id_gen"
)
private long id;
#ManyToOne
#JoinColumn(name = "workstation_name", insertable = false, updatable = false)
private Workstation workstation;
private Timestamp t_start;
private Timestamp t_end;
private Timestamp t_res;
private String owner_email;
private int cpu_reserved;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "cpu_id", referencedColumnName = "id")
private Cpu cpu;
private int ram_reserved;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "ram_id", referencedColumnName = "id")
private Ram ram;
private int disk_reserved;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "disk_id", referencedColumnName = "id")
private Disk disk;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "network_id", referencedColumnName = "id")
private Network network;
private int net_reserved;
}

Related

How to make JPA query method about onetomany?

I'm stuck at deal with this problem. I have 'Review Entity', and 'Heart Entitiy'. And I tried to show them homepage and detailpage separately!
Long countHeartByBookReviewId(Long bookReview_id);
i used jpa query method for showing how many heart it gets in details page..
and now i want to show review descending related to heart count in main page!
how can i make the code..?
#Entity
public class BookReview extends Timestamped {
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Id
private Long id;
...
#Column
private String review;
#JoinColumn(name = "member_id", nullable = false)
#ManyToOne(fetch = FetchType.EAGER)
private Member member;
#OneToMany(mappedBy = "bookReview" , cascade = CascadeType.REMOVE)
private List<Comment> comment;
#JsonIgnore
#OneToMany(mappedBy = "bookReview", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
private List<Heart> heart;
and the other entitiy is here.
public class Heart {
#GeneratedValue(strategy = GenerationType.AUTO)
#Id
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "bookReview_id")
private BookReview bookReview;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "member_id")
private Member member;
and this is function for get menthod...
public ResponseDto<?> getHome() {
List<BookReview> book_review = book_reviewRepository.findAllByOrderByHeartDesc();
List<HomeResponseDto> book_reviewResponseDtoList = new ArrayList<>();
for (BookReview home : book_review) {
book_reviewResponseDtoList.add(HomeResponseDto.builder()
.id(home.getId())
.username(home.getMember().getUsername())
.thumbnail(home.getThumbnail())
.title(home.getTitle())
.author(home.getAuthor())
.publisher(home.getPublisher())
.review(home.getReview())
.heart(heartRepository.countHeartByBookReviewId(home.getId()))
.createdAt(home.getCreatedAt())
.modifiedAt(home.getModifiedAt())
.build()
);
}
return ResponseDto.success(book_reviewResponseDtoList);
}
please help me ......

Spring data JPA one to Many / Many to one not inserting/updating details in database

Am doing one small activity of Teach and address relationship for one to many and in address block there will be one to one relationship between country, district, tahasil etc. Whenever am hitting api and to save it it's not updating or inserting Address in address table.
Detail is
#Entity
#Table(name = "teachers")
#PrimaryKeyJoinColumn(name = "user_id")
public class Teacher extends User {
#Size(min = 3, max = 50)
#Column(name = "first_name")
private String firstName;
#Size(min = 3, max = 50)
#Column(name = "middle_name")
private String middleName;
#Size(min = 3, max = 50)
#Column(name = "last_name")
private String lastName;
#OneToMany(fetch = FetchType.LAZY,mappedBy = "teacher")
#JsonManagedReference
private Set<Address> addresses = new HashSet<>(0);
Getter Setter...
}
Then Address Entity
#Entity
#Table(name = "address")
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "address_id")
private Long addressId;
#ManyToOne (fetch = FetchType.LAZY)
#JoinColumn(name = "user_id", nullable = false)
#JsonBackReference
private Teacher teacher;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "country_id", referencedColumnName = "country_id")
private Country country;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "state_id", referencedColumnName = "state_id")
private State state;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "district_id", referencedColumnName = "district_id")
private District district;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "tahasil_id", referencedColumnName = "tahasil_id")
private Tahasil tahasil;
#Column(name = "line_one")
private String lineOne;
#Column(name = "line_two")
private String lineTwo;
#Column(name = "landmark")
private String landmark;
#Column(name = "pincode")
private Integer pincode;
public Country getCountry() {
return country;
}
Other Getter Setter
The Country example same to state, district and tahasil
#Entity
#Table(name = "countries", uniqueConstraints = { #UniqueConstraint(columnNames = { "country_name" }) })
public class Country {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "country_id")
private Long countryId;
#NotBlank
#Size(min = 3, max = 50)
#Column(name = "country_name")
private String countryName;
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "country")
private Address address;
Getter Setter
Finally in controller am doing like
Optional<Teacher> teacher = teacherRepo.findByUserId(id);
if (!teacher.isPresent())
return ResponseEntity.notFound().build();
teacher.get().setUserId(id);
teacher.get().setFirstName(teacherUpdateForm.getFirstName());
teacher.get().setMiddleName(teacherUpdateForm.getMiddleName());
teacher.get().setLastName(teacherUpdateForm.getLastName());
teacher.get().setAddresses(teacherUpdateForm.getAddresses());
userRepository.save(teacher.get());
Tried so may ways by referring multiple sites and readouts, but still not able to see any insert or update to address table. Please help me to get my mistake.
Regards,
Chetan
You need to cascade the persist of the Teacher entity.
Update the definition of the attribute Address inside the Teacher entity:
#OneToMany(fetch = FetchType.LAZY,mappedBy = "teacher", cascade = CascadeType.ALL)
#JsonManagedReference
private Set<Address> addresses = new HashSet();
You can play with the cascade type value as you want.

Hibernate throws "More than one row with the given identifier was found" once, but not again after that

I have an application with a history table mapped to a view on a ManyToOne relation as follows
#Entity
#Table(name = "HISTORY_TABLE", schema = "MY_SCHEMA")
public class HAction implements Serializable {
...
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "ID_WHO", referencedColumnName = "ID_USER"),
#JoinColumn(name = "CT_WHO", referencedColumnName = "CT_SEQUENCE")
})
private VwUsers whoDidTheAction;
...
//Getters & Setters
}
When running the application, It encountered the famous "More than one row with the given identifier was found".
Therefore, I went into debug to figure out what was the user who was causing this issue. Indeed, it has a duplicate on the database View object. However, during debug, I stumbled upon the following "magic"
Not sure if it has anything to do with my IDE but when using the code inspector on intelliJ (Display on Eclipse) and issuing the following
hAction.getWhoDidTheAction().getId();
It gave me the error, but when doing it for a second time, on the same display "session", no error happened, the result was there as expected.
Anyone knows the source of this behaviour? I couldn't find anything related to this.
full HAction class:
#Entity
#Table(name = "HISTORY_TABLE", schema = "MY_SCHEMA")
public class HAction implements Serializable {
/** */
private static final long serialVersionUID = -294276241063426049 L;
#Id
#Column(name = "ID_HISTORY_TABLE")
#SequenceGenerator(name = "SQ_HISTORY_TABLE", sequenceName = "MY_SCHEMA.SQ_HISTORY_TABLE", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SQ_HISTORY_TABLE")
private Long idHAction;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ID_ACTION", referencedColumnName = "ID_ACTION")
private SAction action;
#Transient
private Long idActionTransient;
#Enumerated(EnumType.STRING)
#Column(name = "CS_OPERATION")
private OperationEnum csOperation;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "DT_OPEARATION")
private Date dhOPeration;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ID_USER", referencedColumnName = "ID_USER")
private VwUser user;
#Column(name = "CD_PROTOCOL")
private String cdProtocol;
#Temporal(TemporalType.DATE)
#Column(name = "DT_PROTOCOL")
private Date dtProtocol;
#Column(name = "DS_ACTION")
private String dsAction;
#Temporal(TemporalType.DATE)
#Column(name = "DT_PREDICTION")
private Date dtPrediction;
#Enumerated(EnumType.STRING)
#Column(name = "CS_ORIGIN")
private OriginEnum csorigin;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ID_REQUIRED_BY_SECTOR", referencedColumnName = "ID_SECTOR")
private VwSector vwSector;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ID_REQUIRED_BY_HQ", referencedColumnName = "ID_HQ")
private VwHq vwHq;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ID_REQUIRED_BY_LOCAL", referencedColumnName = "ID_LOCAL")
private VwLocal vwLocal;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ID_REQUIRED_BY_ASSOC", referencedColumnName = "ID_ASSOC")
private VwAssoc vwAssoc;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ID_REQUIRED_BY_INTER", referencedColumnName = "ID_INTER")
private VwInter vwInter;
#Column(name = "DAY_VL")
private Double dayValue;
#Column(name = "MAT_VAL")
private Double matValue;
#Column(name = "OP_VALUE")
private Double opValue;
#Column(name = "TERC_VALUE")
private Double tercValue;
#Column(name = "TERC_SERV_VALUE")
private Double tercServValue;
#Column(name = "PUB_VALUE")
private Double pubValue;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ID_RESPONSIBLE", referencedColumnName = "ID_UNIT")
private Units unit;
//This is the first fragment
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "ID_BOSS", referencedColumnName = "ID_USER"),
#JoinColumn(name = "CT_BOSS", referencedColumnName = "CT_SEQUENCE")
})
private VwUsers whoDidTheActionBoss;
//
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "ID_SUB_BOSS", referencedColumnName = "ID_USER"),
#JoinColumn(name = "CT_SUB_BOSS", referencedColumnName = "CT_SEQUENCE")
})
private VwUsers whoDidTheActionSubBoss;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ID_PHONE", referencedColumnName = "ID_PHONE")
private VwPhone vwPhone;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ID_EMAIL", referencedColumnName = "ID_EMAIL")
private VwEmail vwEmail;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ID_EMAIL_SUB_BOSS", referencedColumnName = "ID_EMAIL")
private VwEmail vwEmailSubBoss;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ID_PHONE_SUB_BOSS", referencedColumnName = "ID_PHONE")
private vwPhone vwPhoneSubBoss;
#Column(name = "PROTOCOL")
private String protocol;
#Enumerated(EnumType.STRING)
#Column(name = "CONN")
private VwConn conn;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ID_TYPE", referencedColumnName = "ID_TYPE")
private SType type;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "id.HAction")
private List < SFiles > files;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "id.HAction")
private List < SThemes > themes;
#Column(name = "ST_ARCHIVED")
private Character archived;
#Temporal(TemporalType.DATE)
#Column(name = "DT_VIG")
private Date dtVig;
/**
* Create Object {#link HAction}.
*
*/
public HAction() {}
//Getters & Setters
}
After some more in-depth research with a few coworkers I've been pointed that this might be a proxy implementation behaviour, and indeed it was.
During the "getId()" execution the proxy class checks if the many-to-one relation is satisfied, if not, it fills the result with the data of the first found result THEN throws the exception.
This is specially usefull for debugging since it gives you at least one of the duplicate objects. It is the intended behaviour.
Since I already have an object in memory, there is no need to "re-search" the database for the records, therefore the "getId()" method returns the id of the first element and does not throw an exception since it did not query the database again.
Some explanation regarding proxy behaviour can be found here
Some information on the Lazy Initializer can be found here
Thank you to everyone who took their time to help me.

Many-to-many relationship with composite keys, extra column in Hibernate

I have 3 data table:
Applications {id_app, version, name}
Customers {org_id, name}
Associations {id_app, version, org_id, state}.
Applications has a composite primary key (id_app, version), the primary key for Customers is org_id and Associations has a composite primary key (id_app, version, org_id).
In my java application I have the following classes:
#Entity
#Table(name = "Applications")
#IdClass(ApplicationId.class)
public class Application implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "ID_APP", nullable = false)
private String idApp;
#Id
#Column(name = "VERSION", nullable = false)
private String version;
#Column(name = "NAME")
private String name;
#OneToMany(mappedBy = "idPk.appPk", fetch = FetchType.LAZY) // cascade = CascadeType.ALL)
private List<Association> custApps;
// getters and setters
}
public class ApplicationId implements Serializable {
private static final long serialVersionUID = 1L;
private String idApp;
private String version;
//hashcode and equals
}
#Entity
#Table(name = "CUSTOMERS")
public class Customer implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "ORG_ID", unique = true, nullable = false)
private Integer orgID;
#Column(name = "NAME")
private String name;
#OneToMany(mappedBy="idPk.customerPk", fetch = FetchType.LAZY)
private List<Association> custApps;
//getters and setters
}
#Entity
#Table(name = "ASSOCIATIONS")
#AssociationOverrides({
#AssociationOverride(name = "idPk.appPk", joinColumns = #JoinColumn(name = "ID_APP")),
#AssociationOverride(name = "idPk.appPk", joinColumns = #JoinColumn(name = "VERSION")),
#AssociationOverride(name = "idPK.customerPk", joinColumns = #JoinColumn(name = "ORG_ID"))
})
public class Association implements Serializable {
private static final long serialVersionUID = 1L;
private AssociationId idPk = new AssociationId();
private String state;
public Association() {
super();
}
#EmbeddedId
public AssociationId getIdPk() {
return idPk;
}
#Transient
public Customer getCustomerPk() {
return idPk.getCustomerPk();
}
#Transient
public Application getAppPk() {
return idPk.getAppPk();
}
#Column(name = "STATE")
public String getState() {
return state;
}
//setters , hashCode and equals
}
#Embeddable
public class AssociationId implements Serializable {
private static final long serialVersionUID = 1L;
private Application appPk;
private Customer customerPk;
// here is the problem ?
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumns({ #JoinColumn(name = "ID_APP", referencedColumnName = "ID_APP"),
#JoinColumn(name = "VERSION", referencedColumnName = "VERSION") })
public Application getAppPk() {
return appPk;
}
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name="ORG_ID")
public Customer getCustomerPk() {
return customerPk;
}
//setter, hashCode and equals
}
What are the correct annotation? The relationship is many to many between Application and Customers and I create the Association table for that and for the extra column "state".
Now I receive this error: A Foreign key refering sla.model.Application from sla.model.Association has the wrong number of column. should be 2 .
Please help.
Done. I change the following:
In Association class:
#AssociationOverrides({
#AssociationOverride(name = "idPk.appPk", joinColumns = { #JoinColumn(name = "ID_APP", referencedColumnName = "ID_APP"),
#JoinColumn(name = "VERSION", referencedColumnName = "VERSION") }),
#AssociationOverride(name = "idPK.customerPk", joinColumns = #JoinColumn(name = "ORG_ID"))
})

Select records without parent with Hibernate using Criteria API

How select records without parent with Hibernate using Criteria API?
Here is my Java code for select with parents
getSessionFactory().getCurrentSession().createCriteria(Category.class).add(
Restrictions.eq("parent", new Category(parentId))).list();
Category Java code
#Entity
#Table(name = "CATEGORY")
public class Category implements NamedModel{
#Id
#Column(name = "CATEGORY_ID")
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#OneToOne(cascade = CascadeType.REMOVE, fetch = FetchType.LAZY)
#JoinTable(name = "CATEGORY_RELATIONS",
joinColumns = {
#JoinColumn(name = "CATEGORY_RELATIONS_CATEGORY_ID", referencedColumnName = "CATEGORY_ID")},
inverseJoinColumns = {
#JoinColumn(name = "CATEGORY_RELATIONS_PARENT_ID", referencedColumnName = "CATEGORY_ID")})
private Category parent;
#OneToMany(cascade = CascadeType.REMOVE, fetch = FetchType.EAGER, mappedBy = "parent")
private List<Category> children;//...
}
CategoryRelations Java code
#Entity
#Table(name = "CATEGORY_RELATIONS")
#IdClass(CategoryRelations.CategoryRelationsPrimaryKey.class)
public class CategoryRelations implements Serializable {
#Id
#Column(name = "CATEGORY_RELATIONS_CATEGORY_ID")
private long categoryId;
#Id
#Column(name = "CATEGORY_RELATIONS_PARENT_ID")
private long parentId;
#Entity
#IdClass(CategoryRelationsPrimaryKey.class)
public static class CategoryRelationsPrimaryKey implements Serializable {
private long categoryId;
private long parentId;
}
}
You can use Restriction#isNull(propertyName) function for your requirements.
Restrictions.isNull("parent")

Categories