Two tables connected via Primary Key - spring-boot

I have read about the use of #MapsId and #PrimaryKeyJoinColumn annotations, which sounds like a great options. I have two tables (UserList and UserInformation) which have a child, parent relationship, respectively; both classes below are abbreviated to just include the relevant columns. UserInformation's primary key value is always null and does not take the value of its parent column.
User Class
#Entity
#Data
#Table(name = "user_list")
public class UserList {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
// List of foreign keys connecting different entities
#OneToOne(mappedBy = "user")
#MapsId("id")
private UserInformation userInfo;
}
UserInformation Class
#Entity
#Data
#Table(name = "user_information")
public class UserInformation implements Serializable {
#Id
private Integer userId;
#OneToOne
private UserList user;
}
I would prefer to not use an intermediary class if possible. I'm not tied to MapsId or even this implementation if there is a better solution.
Thanks!

The question is not very clear to me, but I think you could improve the following in the modeling of the entity:
The #column annotation can only be omitted when the class parameter is called exactly the same as the database column, taking into account the table name nomenclature, could it be that the column is user_id ?, if so the id parameter should be :
#Id
#column(name="USER_ID")
private Integer userId;
In the user entity being id, it will match the DB ID field so the #column annotation is not necessary

Related

One To One Mapping Spring Data JPA

I've a question about One to One unidirectional Mapping in Spring Boot.
I've a Customer class with a One to One unidirectional mapping to an Address class.
But when I try to associate a new customer with an existing Address, the database is updated.
So two Customers are now associated with the one Address.
As I understand it only one Customer should be associated with one unique Address. Do I understand the concept correctly, or am I doing something wrong in Spring Boot/ Spring Data JPA/ Hibernate?
Customer
#Entity
public class Customer {
#Id
private Long cId;
private String cName;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name="aid")
private Address cAddr;
:
}
Address
#Entity
public class Address {
#Id
private Long aid;
private String town;
private String county;
:
}
data.sql
insert into address values (100, "New York", "NY");
insert into customer values (1, "John Smith", 100);
Application.java
#Override
public void run(String... args) throws Exception {
Customer c1 = new Customer((long)5, "Mr. Men");
Optional<Address> a100 = ar.findById((long)100);
c1.setcAddr(a100.get());
cr.save(c1);
}
Database
There are 2 options on how to make #OneToOne relation: unidirectional and bidirectional: see hibernate doc.
When you scroll down a little bit you will find the following:
When using a bidirectional #OneToOne association, Hibernate enforces the unique constraint upon fetching the child-side. If there are more than one children associated with the same parent, Hibernate will throw a org.hibernate.exception.ConstraintViolationException
It means that you'll have the exception only on fetching and when you have a bidirectional association. Because Hibernate will make an additional query to find the dependent entities, will find 2 of them, which doesn't fit #OneToOne relation and will have to throw an exception.
One way to "fix" uniqueness for your entities, is to make cAddr unique:
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name="aid", unique=true)
private Address cAddr;
If you create your db tables, by setting hbm2ddl property this will add a unique constraint to the aid column.
I really recommend to read the following:
#OneToOne javadoc itself provides examples of how to do everything correctly (for you Examples 1 and 2 are the most useful)
Check Vlad's blog about #OneToOne. It must be the best you can find. At least jump to the chapter "The most efficient mapping" and implement it bidirectional and sharing the PK, using #MapsId.
Also maybe you will come up to the idea to use #ManyToOne option (at least i can imagine that customer can have multiple addresses)
This is not One-to-Many relation. It's One-to-Many as One object has multiple related objects. Checkout this article.
Example:
Post.java
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table
public class Post {
#Id
#GeneratedValue
#Column(name = "post_id")
private Long id;
#Column
private String postHeader;
#OneToMany(
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<Comment> comments = new ArrayList<>();
public void addComment(Comment comment) {
comments.add(comment);
}
public void removeComment(Comment comment) {
comments.remove(comment);
}
// equals() and hashCode()
}
Comment:
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table
public class Comment {
#Id
#GeneratedValue
#Column(name = "postcom_id")
private Long id;
#Column
private String text;
// equals() and hashCode()
}
Check out step "3. Uni-directional one-to-one mapping demonstration" at this site basically carbon copy of what you're trying to do.

How to load insert scripts for multiple tables which are linked with foreign key in spring boot

I am using spring boot. I am loading test data through yml by defining spring.datasource.data=classpath:/test-data/sql_file_EntityOne.sql, classpath:/test-data/sql_file_EntityTwo.sql,...
For every single entity it works seamlessly but problem comes when EntityOne and EntityTwo have foreign key relationship and the corresponding insert statements are written in 2 different SQL files as depicted above.
I am using in memory h2 dB for local.
sql_file_EntityOne.sql
(Id_One, data_1,data_2) values(101, 'dat', 5)
sql_file_EntityTwo.sql
(Id_two, Id_Onethis is fk, data_3,data_4)
values(1,101, 'dat2', null, 5)
EntityOne
#Id
IdOne
....
#OneToMany(Cascade.All, mappedBy="entityOneRef")
List entityTwoRef
EntityTwo
#Id
IdTwo
....
#ManyToOne(Cascade.All)
#JoinColumn("entityTwoRef")
EntityOne entityOneRef
Can you please mention the error you are getting here?
You can use the following hibernate annotations for bidirectional relationship:
#OneToMany(mappedBy = ) on parent enity
#ManyToOne #JoinColumn(name = , nullable = false) on child entity
for example, let's take an example of Cart and Item as two entities with a Cart related as one to many with item:
//Cart
#Entity
#Data
public class Cart {
#Id
private Integer cartId;
#Column
private String data;
#OneToMany(mappedBy = "cart")
private Set<Item> items;
}
//Item
#Entity
#Data
public class Item {
#Id
private Integer itemId;
#Column
private String data;
#ManyToOne
#JoinColumn(name = "cart_id", nullable = false)
private Cart cart;
}
#Data is just lombok annotation for getters and setters. Scripts as below:
//Script1
INSERT INTO CART(cart_id,data) VALUES (101,'data1')
//Script2
INSERT INTO ITEM(item_id,cart_id,data) VALUES (1,101,'data2')
Then load the scripts in spring-boot in order:
spring.datasource.data=classpath:sql_script1.sql,classpath:sql_script2.sql
Hope it helps :)

When does #GenerateValue in hibernate execute

I am using an #Entity with a CrudRepository to create an entry in my MySQL database, and I was wondering at what point the #GeneratedValue(strategy = GenerationType.AUTO) execute and generate the auto increment value?
#Entity
#Table(name = "all_contacts")
public class Contact {
//Ideally this would be set as foreign key to application's user schema
private long userId;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column( name="contactid")
private long contactId;
#NotNull
private String name;
//getters and setters
}
//
public interface ContactRepository extends CrudRepository<Contact, Long> { }
I ask because I want to access the value of the contactId through its getter, but do I have to wait until the ContactRepository.save() is called?
We can't know the new assigned id of that entity prior to executing the SQL INSERT statement.
So, yes you need to ContactRepository.save() or any command that trigger SQL INSERT statement before can get that id. But save is better because it is guaranteed that it will always return ID.
We get the generated value once SQL insert statement is executed. We can't know the value is being assinged before save(). GenerationType.AUTO number generator generates automatic object IDs for entity objects and this generated numeric value is used for primary key field.
#Entity
public class Entity {
#Id #GeneratedValue(strategy=GenerationType.AUTO) int id;
}

Usual field as foreign key

I have two tables. I want to make between them relationship, but the thing is that the child table connects to an attribute in a parent node, which is not a PK. How can I assign a non-PK field as a FK for a table?
Here are the tables. User Information:
#Entity
#Table(name="user")
public class userinformation implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="USER_ID")
private int uID;
#Column(name="LIB_ID")
private String libID;
//Other attributes
}
Lib Information
#Entity
#Table(name="libinfo")
public class Auth {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="AUTH_ID")
private int authID;
#Column(name="LIB_ID")
private String lib_ID;
//Other attributes
}
They both should be linked through libID (surely unique). Any idea how to implement it correctly?
Given:
class User {
#Column(name = "lib_id")
private String libID;
}
you must map the Auth entity as:
class Auth {
#JoinColumn(name = "lib_id", referencedColumnName = "lib_id")
#ManyToOne
private User user;
}
Basically, referencedColumnName is used to inform the JPA provider that it should use a column other than the primary key column of the referenced entity (which is used by default with #ManyToOne mappings).

JPQL Special Query

I have two entity bean :
#Entity
public class User {
#Id
#GeneratedValue
private Long id;
#OneToMany(mappedBy="user", cascade = CascadeType.ALL)
private List<Comment> comments = new ArrayList<Comment>();
//SOME OTHER CLASS VARIABLES
//GETTERS AND SETTERS
}
and my Comment class is like this :
#Entity
public class Comment {
#Id
#GeneratedValue
private Long id;
private String title;
private String content;
#ManyToOne
private User user
//SOME OTHER CLASS VARIABLES
//GETTERS AND SETTERS
}
now I know that I can get the User Object from session and set the user for my comment like this in order to be able to use the join feature in JPA:
commentObject.setUser(TheSessionGrabedUserObject/UserObjectWhichHasFetchedFromDbUsingUserId);
but as long as I have the userId for my user Object I do not need to do this.
I'm looking for a way to insert this foreignKey into my comment table without getting the User Object from session or maybe query to database to fetch it first !
how I'm gonna do it using JPQL ?
You can use the entityManager.getReference() method. In your case:
entityManager.getReference(User.class, userId);
This will not perform any DB query, but will give you a User instance with only the ID populated, and you can pass that to commentObject.setUser().

Resources