Hibernate not setting foreign key - java

I'm trying to learn Hibernate with this simple example but I'm having so trouble with the foreign key which remains "null" in the database.
#Entity
#Table(name = "tb1")
public class Track {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name="id_tb1", unique= true)
private int id_tb1;
#Column(name = "title")
private String title;
#ManyToOne
#JoinColumn(name="id_tb2")
private tb2 cd;
And this is the second class
#Entity
#Table(name = "tb2")
public class CD {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name="id_tb2", unique = true)
private int id_tb2;
#Column(name="title")
private String title;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL,mappedBy = "cd")
private List<tb1> tracks = new ArrayList<tb1>();
I save like this:
SessionFactory factory = new Configuration().configure("/resources/hibernate.cfg.xml").buildSessionFactory();
Session session1 = factory.openSession();
session1.beginTransaction();
session1.save(tb2);
session1.getTransaction().commit();
but when Isavethe id_tb2 (in the table tb1) is not set and it remains null. What I'm missing?

The problem you have to set the relation on both sides for a bidirectional relationship.
So you have to set your relationship forCD and your Track object and persist/merge them afterwards.
Without seeing to much of your code you have to do something like.
cd.getTracks().add(track);
track.setCD(cd);
session1.save(track);
session1.save(cd);
See another question for more details.

I think your type of the table2
private tb2 cd;
should be changed as
private CD cd;

Related

JPA one to one mapping creates multiple query when child entity is not found

I have a parent entity 'contracts' that has a one-to-one relation with another entity 'child-contract'. the interesting thing is that the mapping field ('contract_number')id not a primary key-foreign key but is rather a unique field in both the tables. Also it is possible for a contracts to not have any child contract altogether. With this configuration I have observed hibernate to generate 1 additional query every time a contracts does not have a child-contract. I filed this behavior very strange. Is there a way to stop these unnecessary query generation or have I got something wrong.
below is a piece of my code configuration.
#Data
#Entity
#Table(name = "contracts")
public class Contracts implements Serializable {
#Id
#JsonIgnore
#Column(name = "id")
private String id;
#JsonProperty("contract_number")
#Column(name = "contract_number")
private String contractNumber;
#OneToOne(fetch=FetchType.EAGER)
#Fetch(FetchMode.JOIN)
#JsonProperty("crm_contracts")
#JoinColumn(name = "contract_number", referencedColumnName = "contract_number")
private ChildContract childContract ;
}
#Data
#NoArgsConstructor
#Entity
#Table(name = "child_contract")
#BatchSize(size=1000)
public class ChildContract implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#JsonProperty("id")
#Column(name = "id")
private String id;
#JsonProperty("contract_number")
#Column(name = "contract_number")
private String contractNumber;
}
Please help.
Thank-you
You can use NamedEntityGraph to solve multiple query problem.
#NamedEntityGraph(name = "graph.Contracts.CRMContracts", attributeNodes = {
#NamedAttributeNode(value = "crmContract") })
Use this on your repository method as
#EntityGraph(value = "graph.Contracts.CRMContracts", type = EntityGraphType.FETCH)
// Your repo method in repository

Hibernate #ManyToMany association table with extra field that can be queried differently

I was trying to do #ManyToMany association and it worked when I tried to do relations like
User can have multiple group and one group can multiple user.. it worked ,and hibernate created custom table based on it automatically and it did its worked. later I had to add more columns to the association table so I followed a article and set the things up as per that, which worked pretty good.
I am using SpringBoot and is using SpringDataJPA
Here is my implementation :
#Entity
#Table(name = "USERS")
public class User {
#Id
#GeneratedValue
private long id;
private String username;
private String password;
private String email;
#OneToMany(mappedBy = "user")
private Set<UserGroup> userGroups = new HashSet<UserGroup>();
}
#Entity
#Table(name = "GROUPS")
public class Group {
#Id
#GeneratedValue
private long id;
private String name;
   #OneToMany(mappedBy = "group")
private Set<UserGroup> userGroups = new HashSet<UserGroup>();
}
#Entity
#Table(name = "USERS_GROUPS")
public class UserGroup {
#Id
#GeneratedValue
private long id;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "USER_ID")
private User user;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "GROUP_ID")
private Group group;
// additional fields
private boolean activated;
private Date registeredDate;
}
User user = new User("tommy", "ymmot", "tommy#gmail.com");
Group group = new Group("Coders");
User persistedUser = userRepository.save(user);
Group persistedGroup = groupRepositry.save(group);
UserGroup userGroup = new UserGroup();
userGroup.setGroup(persistedGroup);
userGroup.setUser(persistedUser);
userGroup.setActivated(true);
userGroup.setRegisteredDate(new Date());
userGroupRepository.save(userGroup);
Now how to write a SpringData equavalent methd name for getting user's
group where the user is active ? i.e I make user active = false when
some one deletes users from a group instead of deleting the entry from
the user_group assossiation table.
Can we do it on the userRepository?
I think that you would like to have repository similar to this one:
public interface UserGroupRepository extends JpaRepository<UserGroup, Long> {
List<UserGroup> findAllByUserAndActivatedIsTrue(User user);
}
This method will give you List of all groups that this user is assigned to and is active.
If you would like to parameterize also activated field, you should instead use
List<UserGroup> findAllByUserAndActivated(User user, boolean activated);
I hope that this helps you. Good luck.
And btw, I recommend reading this:
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods
Helps a lot

JPA - 2 #ManyToMany relationships between 2 entities (one of them with extra Entity)

I am trying to solve JPA problem. I have 2 main entities - CameraItem and Chain (which represents ordered list of cameras)
Now there have to be 2 #ManyToMany relationships between CameraItem and Chain.
Each CameraItem has at least one parent Chain. As one CameraItem can belong to different Chains, and each Chain can have multiple CameraItems this is the first simple direct #ManyToMany relationship.
Chains can be connected with each other via CameraItem. In other words, CameraItem is holding the connection between Chains. But this is not simple #ManyToMany relationship, because we also need information about direction of the Chains connection. So it is #ManyToMany relationship with new Entity as Baeldung describes here https://www.baeldung.com/jpa-many-to-many. Entity ConnectionPoint is holding the information about the direction as a String.
I paste the classes here:
CHAIN CLASS:
#Entity
#Table(name = "chain")
public class Chain {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#NotBlank(message = "Chain name is mandatory")
private String name;
#Column(name = "PLANT_NAME")
private String plantName;
private String description;
private String status;
private Boolean hasPlant;
#CreationTimestamp
#Column(name = "creation_time")
private LocalDateTime creationTime;
#OneToMany(mappedBy = "camera_item")
private List<CameraItem> cameraItems = new ArrayList<>();
#OneToMany(mappedBy = "chain")
Set<ConnectionPoint> connectionPoints;
CAMERA ITEM CLASS:
#Entity
#Table(name = "camera_item")
public class CameraItem {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#ManyToOne
#JoinColumn
private Camera camera;
private String name;
private Integer positionInChain;
#ManyToMany(mappedBy = "cameraItems", fetch = FetchType.LAZY)
private List<Chain> parentChainIds;
#OneToMany(mappedBy = "cameraItem")
Set<ConnectionPoint> connectionPoints;
CONNECTION POINT CLASS:
#Entity
#Table(name = "connection_point")
public class ConnectionPoint {
#Id
private Long id;
#Column(name = "direction")
private String direction;
#ManyToOne
#JoinColumn(name = "chain")
private Chain chain;
#ManyToOne
#JoinColumn(name = "camera_item")
private CameraItem cameraItem;
When I run the application I get this error:
org.hibernate.AnnotationException: mappedBy reference an unknown
target entity property:
no.trafsys.videodashboard.model.entity.CameraItem.camera_item in
no.trafsys.videodashboard.model.entity.Chain.cameraItems
Does somebody know where the problem can be?
I use #OneToMany annotations in Chain and CameraItem entities and #ManyToOne in ConnectionPoint like Baeldung in his tutorial.
Thank you in advance for any help
I don't think there is issue in ConnectionPoint. I think the issue is that:
In Chain class,
#OneToMany(mappedBy = "camera_item") // One-to-Many defined here
private List<CameraItem> cameraItems = new ArrayList<>();
while in CameraItem class, corresponding property is defined as follow:
#ManyToMany(mappedBy = "cameraItems", fetch = FetchType.LAZY) // Many-To-Many
private List<Chain> parentChainIds;
Try changing the mapping type to #ManyToMany in Chain class as well. It might work.
PS: I am not entirely sure of this, but this feels like the issue[incorrect mapping type]. Wanted to add this as a comment, but due to space issues, adding it as an answer.
#Entity
#Table(name = "chain")
public class Chain {
//..
#OneToMany(mappedBy = "camera_item")
private List<CameraItem> cameraItems = new ArrayList<>();
//..
}
mappedBy parameter can only be in one side of the relation. I suspect camera_item is database table column name. So your cameraItems needs #JoinTable(name = "camera_item"... annotation

Problem with #ManyToOne map in EclipseLink

I'm having trouble with this #ManyToOne map, searched a lot, but still can't find a solution for this problem.
I have these two classes, i will never insert anything into TB_MANUAL, i'll just use it as reference for the CD_MANUAL field in TB_COMPANY, like this:
Company company = new Company();
company.setManual("2"); //Theres already a row with this id in the TB_MANUAL
and then persist company, but i got this error:
Caused By: java.lang.IllegalStateException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST: 2.
at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.discoverUnregisteredNewObjects(RepeatableWriteUnitOfWork.java:313)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.calculateChanges(UnitOfWorkImpl.java:723)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabaseWithChangeSet(UnitOfWorkImpl.java:1516)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.issueSQLbeforeCompletion(UnitOfWorkImpl.java:3168)
at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.issueSQLbeforeCompletion(RepeatableWriteUnitOfWork.java:355)
Truncated. see log file for complete stacktrace
-
#Entity
#Table(name = "TB_COMPANY", schema = "ADMPROD")
#Cacheable
public class Company implements Serializable {
private static final long serialVersionUID = 1L;
public Company() {}
public Company(String id) {
this.id = id;
}
#ManyToOne
#JoinColumn(name = "CD_MANUAL", referencedColumnName = "CD_MANUAL", nullable
= true)
private Manual manual;
public void setManual(String idManual) {
this.manual = new Manual(idManual);
}
}
and
#Entity
#Table(name = "TB_MANUAL")
public class Manual implements Serializable{
private static final long serialVersionUID = 1L;
public Manual() {
}
public Manual(String id) {
this.id = id;
}
#Id
#Column(name = "CD_MANUAL")
private String id;
#Column(name = "DS_OBS_MANUAL")
private String description;
}
You create new Manual every time you set it, so your object is detach from EntityManager, or has not data at all.
I don't argue if that is a good design (althought I've never would do it like that), to over come your problem you should add CascadeType.PERSIST to your relation.
#ManyToOne
#JoinColumn(name = "CD_MANUAL", referencedColumnName = "CD_MANUAL", nullable
= true, cascade = CascadeType.PERSIST)
private Manual manual;
The problem was in the Manual table primary key, the JPA doesnt find any row with id 1 because the primary key of Manual is char(2), passing "1 " instead of "1" solved the problem.

How to insert in to multiple tables via Hibernate

I have two tables I need to insert in to in Hibernate - I have a User and every user belongs is a Member. Therfore when creating a new user I need a new entry in the Member table. I have attempted this via creating a Member object which maps to my member table and then having that as a field in my User object which maps to the user table
#Entity
#Table(name = "USER")
public class User
{
#Id
#GeneratedValue
#Column(name = "id")
private int id;
#Column(name = "username")
private String username;
#Column(name = "password")
private String password;
#Column(name = "fullName")
private String fullName;
//other fields ommited
#OneToOne
#JoinColumn(name = "id")
private Member member;
My member pojo looks as follows
#Entity
#Table(name = "MEMBER")
public class Member
{
#Id
#GeneratedValue
#Column(name = "id")
private int id;
#Column(name = "sgpid")
private int sgpid;
#Column(name = "username")
private String username;
Trying to save the object i do as follows;
#Override
public boolean addUser(User user)
{
if (user == null)
{
throw new IllegalArgumentException("Unable to add null user");
}
Session session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
session.save(user);
session.getTransaction().commit();
return true;
}
This gives me the row saved in my user table but the entry is not inserted in to the member table. I think my linking annotations are probably incorrect but I am not too sure - please could someone provide some assistance.
Thanks
Try to set the cascade value of the #OneToOne annotation:
#OneToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name = "id")
private Member member;
First thing in your user class you should change the joinColumn to member_id.
As mentioned in another answer to persist a related entity you need to set the cascade to persist, i would recommend using cascade All which will involve the related entity in all operations check the doc
https://docs.oracle.com/cd/E19798-01/821-1841/bnbqm/index.html
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "member_id")
private Member member;

Categories