I have a problem with hibernate relationship many-to-one.
My system is commercial proposal controller, where has a responsible for proposal from user entity. When the proposal is create and set the responsible, has no problems, works fine. But when change the responsible and update, it changes the object, I can see in a datatable, but has no update in database. If I refresh on page the update disapears.
#Entity
#Table(name = "proposal")
public class Proposal implements Serializable {
#Id
#GeneratedValue
private Integer id;
#ManyToOne
private User responsible;
............
DAO Code
public void update(Proposal proposal) {
this.session.update(proposal);
}
In the User class I don't make any annotation about this relationship, it's a unidirectional relationship. The class proposal yet use the user class to make a user's bag, as participants, and this relationship will be unidirectional relationship too.
I tried to make annotations in user class but no works too.
User class with annotations
#Entity
#Table(name="user")
public class User implements Serializable{
#OneToMany
private List<Proposal> proposal;
The class User has a many-to-one relationship with userType and it works fine.
#Entity
#Table(name="user")
public class User implements Serializable{
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "idType, nullable = true)
private TipoUsuario userType;
Someone has any ideia about this?
Thanks
You'll see changes after flushing to database. Either manually - flush() or after transaction has been committed. The User class - must be either an entity (both #Entity and #Id annotations are needed) or embedable class and should be configured in the corresponding way in this case.
I'm not sure how it works if you say - #Entity without #Id for User, but try again:
Run flush for User. If you won't get any exception - it is a persisatble object. You should see in db/web application log files - sql insert query.
Commit transaction. The updated value of User should be committed.
If you invoke refresh, before commit, all your changes will be lost.
It looks to me like you're missing the #JoinColumn annotation from your ManyToOne relationship with your User. Try updating to this:
#ManyToOne
#JoinColumn(name="userId")
private User responsible;
But obviously replace 'userId' with the id name as its specified in your table.
Related
How to properly delete a record from a database in one query. For example, when an entity uses the primary key of the parent entity using the #MapsId annotation, if the parent entry is deleted, it will swear that the parent's id is used in the child entity.
Code example :
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private long id;
private String name;
}
#Entity
public class UserDetails {
#Id
private long id;
private String phone;
#OneToOne(fetch = FetchType.LAZY)
#MapsId
private User user;
}
Here, when deleting a User using the JpaRepository delete method, an error will occur that the UserDetail uses the primary key User
First, are you sure the direction of the relation makes sense? I would have expected it to be the other way around, because the user ID and name seem to be the more basic info that you need more often.
Second, what you're doing seems like an attempt to optimize performance, because you could just as well store all the data in a single entity. Are you sure the optimization pays off? (I would guess not.) See Premature Optimization.
Third, if the relation was the other way around, you could modify the annotation to #OneToOne(cascade=CascadeType.DELETE) or #OneToOne(cascade=CascadeType.ALL) to let JPA automatically delete the other entity when the first is deleted.
For that you need to delete all foreign keys with used by primary key
or by using cascade
After that use below
In JPA we can use deleteById
or by named query
DELETE FROM EMPLOYEE WHERE ID = ?1
or my native query
delete from employee where id = ?1
I have created a Spring Boot JPA micro service with a MySQL back end. I have two simple entities in my model, users and skills. These are used to represent the skills base within my organisation. Each user may have many skills and so I have used a one to many join to model this and can successfully assign skills to users. In my database, I can see that a users_skills table has been created by JPA to achieve this.
However, I am now struggling because, for each skill that a user has, an experience level needs to be specified (e.g. basic, advanced, expert) and I am unsure how to achieve this. I'm not sure whether 'levels' should just be an enum type within the Skill entity, or perhaps it should be a separate entity in its own right? Could I configure JPA so that it generates a users_skills_levels table which would represent this three-way relationship? Any advice would be most welcome!
These are my Entity classes: -
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
private String name;
private String email;
#OneToMany(
cascade = CascadeType.ALL,
orphanRemoval = true
)
private Set<Skill> skills = new HashSet<>();
getters and setters
}
#Entity
#Table(name = "skills")
public class Skill {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
private String name;
getters and setters
}
That's not possible what you try to achieve.
You should create an Entity for the users_skills_levels. E.g. UserSkillLevel This entity will then have a ManyToOne relationship to User and a ManyToOne relationship to Skills plus the attribute level.
The User has a collection of UserSkillLevel and the Skill entity as well.
Please find a more in-depth example here:
https://thoughts-on-java.org/many-relationships-additional-properties/
I am facing lazy inizialization issue when I added Lombok project into my hibernate project and used its #Getter and #Setter on the entity class.
Entity classes are annotated with #Entity of Javax.persistence as I am using hibernate 5.
Issue stacktrace :-
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:146)
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:259)
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:73)
at com.capehenry.domain.user.User_$$_jvst52e_9.getId(User_$$_jvst52e_9.java)
at com.capehenry.business.rs.course.SeatRequestResource.validateSeatRequestCancel(SeatRequestResource.java:338)
at com.capehenry.business.rs.course.SeatRequestResource.cancel(SeatRequestResource.java:220)
Everything was working fine with below code
#Entity
#Audited
#Table(name = "seat_request")
public class SeatRequest extends BaseEntity {
private CourseSchedule courseSchedule;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "courseScheduleId", nullable = false)
public CourseSchedule getCourseSchedule() {
return courseSchedule;
}
public void setCourseSchedule(CourseSchedule courseSchedule) {
this.courseSchedule = courseSchedule;
}
When I do searRequest.getCourseSchedule().getId() it works at rest layer means outside the transaction.
As soon as I change the code to below (add lombok), searRequest.getCourseSchedule().getId() at rest layer starts throwing lazyInitializationException :-
#Entity
#Audited
#Table(name = "seat_request")
#Setter
public class SeatRequest extends BaseEntity {
#ManyToOne(fetch = FetchType.LAZY, optional=false)
#JoinColumn(name = "courseScheduleId", nullable = false)
private CourseSchedule courseSchedule;
NOTE :-
1) I have to compulsory use Lombok project
2) I have to use searRequest.getCourseSchedule().getId() outside Sevrice and trasaction
Please suggest the solution, Thanks in advance!!
I have to use searRequest.getCourseSchedule().getId() outside Service and transaction
I have noticed this just now... If you are outside the service and transaction you will always have that exception. Try to use FetchType.EAGER and it should work.
When you are out of transaction your entities are detached, this means that all collections that you marked as lazy won't be loaded. So you have two options: the first is to perform all calls to collections getters inside the transaction, the second one is to mark as eager your collection, so when Hibernate loads the entity it will also load referenced collection immediately. Alternatively you could map to a DTO your Entity inside your transaction. As long as you are in the transaction the getters of lazy loaded field will always work, so a mapper to the DTO would access all informations. Once that the DTO is out of the transaction you will have access to all fields you have mapped, and than do whatever you want.
Here is how I solved the issue finally!
I thought the issue started after integration with Lombok project but the issue started when the annotations were moved to field level from method (property) level.
Bear with me for the long answer.
Here foreign is refering to the database level foreign tables.
To access any column from foreign table's outside the transaction you need to either use FetchType.Eager (which is default in hibernate for any foreign object) or need to join/subquery that table.
But if you just want to fetch the foreign key(column) with which 2 tables are joined (in our case the ID) and want to keep FetchType.LAZY then you can do it in 2 ways :-
1) Keep annotations (manyToOne, JoinColumn etc) on getter methods
2) If annotations has to be kept on field level then write one more annotation on foreign key field in parent table which is - #Access(AccessType.PROPERTY)
So in above code to solve I added this annotation on id field of course Schedule
#Entity
#Audited
#Table(name = "course_schedule")
#Getter
#Setter
public class CourseSchedule{
#Id
#GenericGenerator(name = "autoincr", strategy = "native")
#GeneratedValue(generator = "autoincr")
#Column(name = "id", unique = true, nullable = false)
#Access(AccessType.PROPERTY)
protected Long id;
..........
}
So no change was required in Seat Request.
Lets assume I have the following domain model:
users
----
id (PK)
partitionkey (PK)
In the above table, the partition key is primarily used for partitioning. (MySQL requires the partitionkey to be part of the primary key). If we assume the record can be uniquely identified by the id field only, is there any harm in skipping partitionkey in the mapping. For example, is the mapping below valid:
#Entity
#Table(name = "users")
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="id")
public Long id;
#Column(name="partitionkey")
private Long partitionKey;
}
Try to define a separate #Embeddable object with the PK fields and use it as #EmbeddedId in your #Entity class like this:
#Embeddable
public class MyCompositePK {
#Column(name="id")
private Long id;
#Column(name="partitionkey")
private Long partitionKey;
}
#Entity
#Table(name = "users")
public class User implements Serializable {
#EmbeddedId
private MyCompositePK id;
...
}
Yes, it's valid. JPA provider is not aware of any constraints or other features that exist in the tables to which entities are mapped.
However, is it a good approach, especially because we're talking about partitioning here? Keep in mind that entities are associated via ids. So, for each entity that is associated with User, JPA provider will search the associated User instance by id column only, thus partitioning column will not be included in the query. This may or may not be a problem. See this answer as well for more details.
The alternative could be using provider specific extensions like #JoinFormulas in Hibernate, but they may not be easy to get right.
I would say that going with composite ids is the most straightforward solution to go.
I'm working with JPA 2 + Hibernate 4 and I'm implementing some CRUD operations on model entities.
Now I need to prevent a certain entity (EntityB) to be deleted when a related entity (EntityA) exists in database:
#Entity
public class EntityA {
#Id
private int id;
#OneToOne(mappedBy = "entityA", optional = false, fetch = FetchType.LAZY)
private EntityB entityB;
//...
}
#Entity
public class EntityB {
#Id
private int id;
#OneToOne
#JoinColumn(name = "id")
private EntityA entityA;
//...
}
Is there any way to achieve this using relationship options or should I check EntityA existence in my dao/repository before removing EntityB?
NOTE I need this also for #ManyToOne relationships.
If you want to prevent that in your code, than simply do not delete that entity (by checking that manually). There is no possibility to do that with annotations.
On the other side, this sounds to me rather like a need for a DB constraint. If those entities are already related, then simply add a foreign key constraint (if none is existent). If not, than think of adding one.
PS: if you already have a relationship, check the CascadeType.REMOVE setting.
I don't think you can solve this with annotations. You should manally check related-entity existence before.