I am just started with hibernate 4 (annotations), I have following three model Classes with One to One and One to Many Relationship.`
#Entity
#Table(name = "USERS",uniqueConstraints = {#UniqueConstraint(columnNames={"EMAIL_ID"})})
public class UserBasicInfo implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="userId")
private Long userId;
#Column(name="EMAIL_ID")
private String emailId;
#Column(name="PASSWORD")
private String password;
#OneToMany(fetch = FetchType.LAZY, mappedBy="userBasicInfo",cascade = CascadeType.ALL)
private Set<UserDeviceInfo> userDeviceInfo;
#OneToOne(mappedBy="userBasicInfo",cascade = CascadeType.ALL)
private UserAdvancedInfo userAdvancedInfo;
///////// Getter setters
}
and
#Entity
#Table(name = "USERS_ADVANCED_DETAILS")
public class UserAdvancedInfo implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="ADVANCED_INFO_ID")
private Integer advancedInfoId;
#Column(name="GENDER")
private Gender gender;
#Column(name="UNIT_OF_MEASUREMENT")
private UnitOfMeasurement unitOfMeasurement;
#Column(name="HEIGHT")
private Double height;
#OneToOne
#JoinColumn(name="userId", insertable = true, updatable = true, nullable = true)
private UserBasicInfo userBasicInfo;
//////////////////////// Getter and Setter ////////////////////////
}
On the basis of above model classes I have two different REST APIs.
In the first API, only basic info will inserted and that is working fine.
But in the second API I have to update On UserAdvancedInfo on the basis of primary key of UserBasicInfo.
I have used update, merge, saveOrUpdate, but instead of updating data these all methods inserting a new row every time.
Kindly help me out.
Thanks,
To update the entity, you should need to set primary key to UserAdvancedInfo object and set UserBasicInfo object with there primary key to UserAdvancedInfo object :
UserAdvancedInfo info = new UserAdvancedInfo();
UserBasicInfo userInfo =new UserBasicInfo();
info.setUserBasicInfo(userInfo);
Hibernate checks for primary key for updation.
Related
I'm new at Spring Boot's JPA concept so need your help in deciding how to import just the ID of another entity, say User into HealthData entity. Following is my User entity:
#Entity
#Table(name = "user",uniqueConstraints = {#UniqueConstraint(columnNames = "email")})
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable = false)
private String name;
#Email
#Column(nullable = false)
private String email;
private String imageUrl;
#Column(nullable = false)
private Boolean emailVerified=false;
#JsonIgnore
private String password;
#NonNull
#Enumerated(EnumType.STRING)
private AuthProvider authProvider;
private String providerId;
}
And I wish to define HealthData entity in the following manner :
#Entity
#Table(name = "HealthData",uniqueConstraints = {#UniqueConstraint(columnNames = "id")})
public class HealthData {
#Id
private Long id; //how to import id of User here?
#Column
private Double height;
#Column
private Double weight;
#Column
private int age;
...other columns
}
Now, I wish to use Id of User to this entity(kind of making parent-child relationship) . I don't want to add User class object in HealthData. I thought of using #OneToOne in HealthData but then it would add User in it. How can i just include Id from parent table in child table?
In this case, your HealthData has a reference to User, and I'm not sure why you wouldn't have mapped this as a foreign key. If you are able to do so, I'd suggest the following:
#Entity
#Table(name = "HealthData")
public class HealthData {
#Id
#OneToOne
#JoinColumn(name = "id")
private User user;
#Column
private Double height;
#Column
private Double weight;
#Column
private int age;
...other columns
}
JPA then handled setting the "ID" to the value within your user instance for you, and can persist both in the same transaction automatically. Allowing references to be marked as IDs is known as a derived ID and supported I believe since JPA 2.0.
As for efficiency, you can still lazy fetch or even not fetch the user instance. It is simple to just map the ID column as a basic using a slightly different approach:
#Entity
#Table(name = "HealthData")
public class HealthData {
#Id
private Long id;
#MapsId
#OneToOne(optional = false, fetch = FetchType.LAZY)
#JoinColumn(name = "id")
private User user;
#Column
private Double height;
#Column
private Double weight;
#Column
private int age;
...other columns
}
JPA will set both the User id as well as the healthData.id values based on what it generates for the user Id sequence when you set the healthData.user reference.
You can use getters and setters to set the value of user id in the healthdata table.
I am writing an online store using Spring Boot (MVC) and hiberbate. I have an order class where I need a Сart link. But the problem is that in the database I do not have a specific Сart table, but there is a cart _products table, where the peimary key consists of two columns (as shown in the picture below!). I really need a connection in the Order class, so I decided to make a Composite Primary Key at the hibernate level (and I seem to have done it), but I can't figure out what to do next! I am stuck. Please tell me where to go? How can I solve my problem?
OrderClass:
#Entity
#Table(name = "pg_order")
public class Order {
// Fields
//
private #Id
#GeneratedValue
Long id;
private String address;
#Column(name = "phone_number")
private String phoneNumber;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "date_order")
private Date dateOrder;
#Enumerated(EnumType.STRING)
#Column(name = "order_status")
private OrderStatus orderStatus;
#Column(name = "total_cost")
private BigDecimal totalCost;
// Relationships
//
#ManyToOne
#JoinColumn(name = "user_id")
private User user;
// #OneToMany
// #JoinColumn(name = "cart_products_pkey")
// private Cart cart;
}
Cart:
#Entity
#Table(name = "cart_products")
public class Cart {
#Embeddable
#NoArgsConstructor
#AllArgsConstructor
static class CartId implements Serializable {
private Long orderId;
private Long drinkId;
}
// Fields
//
#EmbeddedId
private CartId cartId;
#ManyToOne(optional = false)
#JoinColumn(name = "order_id")
private Order order;
#ManyToOne(optional = false)
#JoinColumn(name = "drink_id")
private Drink drink;
private int count;
}
If you need access to the 'DRINKS' for that order. You need to change the relation to Cart from the Order class.
You have commented a relationship where ORDER just have access to one CART, since you need to access N CARTS (One to Many) you need to add a SET. Something like this:
#OneToMany
#JoinColumn(name = "cart_products_pkey")
private Set<Cart> cartProducts;
Now the ORDER has a SET of CART.
You can easily access to the CARTS of that ORDER with order.getCartProducts()
And since CART has a key to DRINK, you can easily access it.
Hope this can help you.
So basically I have two tables : NaturalPerson which holds personalNumber column and NaturalPersonReserve where I added a new column personalNumber. Both tables have existing data in it and I want to populate my NaturalPersonReserve's new Column personalNumber from naturalPerson
s table ( I mean the existing data to update from One tables's column to second)
NaturalPerson Entity :
#Entity
#Table(name = "naturalperson")
public class NaturalPerson implements Serializable {
#Id
#Column
#GeneratedValue(strategy = GenerationType.AUTO)
private int personId;
#Column(unique = true)
private String personalNumber;
#Column
private String serialNumber;
#Column
private String firstname;
#Column
private String lastname;
#Column
private String birthdate;
#Column
private String gender;
NaturalPersonReserve Entity :
#Entity
#Table(name = "natural_person_reserve")
public class NaturalPersonReserve extends SuperModel{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToOne
#JoinColumn(name = "payment_id")
private PaymentParts payment;
// The relationship
#ManyToOne
#JoinColumn(name = "person_id")
private NaturalPerson person;
#ManyToOne
#JoinColumn(name = "company_id")
private Company company;
#Column(name = "amount", columnDefinition = "DECIMAL(10,2) DEFAULT 0.0")
private double amount;
#Enumerated(EnumType.STRING)
#Column(name = "operation_type")
private EReserveType operationType;
// My added column
#Column(unique = true)
private String personalNumber;
Basically you added a new column, and you want to populate it with data from another table. That's not really a java/spring/hibernate issue. That kind of problem will exist regardless of how your application is built.
I can only see two ways:
Write a SQL statement that will update the table based on a select statement. The syntax may change depending on which DB you use. This post has an Oracle example.
Write a program (in your case probably in Java) that will update each register with the correct data. It will have the same effect as the above option, but it will be implemented in your language of choice.
I have 2 tables, the first one is quite variable, the second one contains only constants:
USER.ID USER.NAME USER.USER_TYPE (FK on USER_TYPE.ID)
INT VARCHAR(64) INT(1)
----------------------------------
1 Alex 3
2 Jane 1
3 Carl 3
USER_TYPE.ID USER_TYPE.VALUE
INT(1) VARCHAR(64)
------------------------------
1 PENDING
2 REGISTERED
3 BANNED
4 ACTIVE
The foreign key USER.USER_TYPE is required and refering to a primary key USER_TYPE.ID in table USER_TYPE (one-to-one relation). Here is my mapping in Hibernate.
User.java
#Entity
#Table(name = "USER")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private int id;
#Column(name = "NAME")
private String name;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "USER_TYPE")
private UserType userType;
}
UserType.java
#Entity
#Table(name = "USER_TYPE")
public class UserType {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private int id;
#Column(name = "VALUE")
private String value;
}
My goal is to keep the enumerated values in the database. How to map UserType's value instead of id to User and validate it? I want to pass the constant VALUE to the String instead of its ID.
private String userType;
The expected result of the first user would be:
User[id=1, name=Alex, userType=Banned]
User[id=2, name=Jane, userType=Pending]
User[id=3, name=Carl, userType=Banned]
My attempt was to use this annotation on definition of table twice with both colums switched
#SecondaryTable(name="USER_TYPE",
pkJoinColumns={#PrimaryKeyJoinColumn(name="ID", referencedColumnName="USER_TYPE")}
)
and get the VALUE with
#Column(table="USER_TYPE", name="VALUE")
private String UserType;
however it leads to the error
Unable to find column with logical name: USER_TYPE in org.hibernate.mapping.Table(USER) and its related supertables and secondary tables
First you need to change the relation from #OneToOne to #ManyToOne as UserType can be used by one or many User and User can have one and one UserType.
Secondly use referencedColumnName which references :
The name of the column referenced by this foreign key column.
So User entity will be:
#Entity
#Table(name = "USER")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private int id;
#Column(name = "NAME")
private String name;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "USER_TYPE", referencedColumnName = "VALUE")
private UserType userType;
}
In UserType you should apply a unique constraint using #NaturalId to value field + do not provide its setter, to prevent duplicate values as It may lead to inconsistency:
#Entity
#Table(name = "USER_TYPE")
public class UserType {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private int id;
#NaturalId
#Column(name = "VALUE")
private String value;
}
Hope it solves the issue!
Enumerations could be simpler:
enum UserType {
PENDING,
REGISTERED,
BANNED,
ACTIVE
}
#Entity
#Table(name = "USER")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private int id;
#Column(name = "NAME")
private String name;
#javax.persistence.Enumerated
private UserType userType;
}
If you really need separated table and #OneToOne relation, you can use #Formula from Hibernate:
#Formula("(select ut.value from user_type ut where ut.ID = USER_TYPE)")
private String userType;
For this really special requirement you could use SecondaryTable annotation.
That is, you don't need UserType entity, but declare attribute userType as String in User entity with column mapping to the secondary table "USER_TYPE".
First of all, I suggest you use ManyToOne relation. and Not CascadeType.ALL if you are not planning update or delete on USER_TYPE table.
If you do not need adding new UserTypes frequently use enum for it. It will just work as you want.
Second solution: As long as fetch = FetchType.EAGER you can add A transient field and return value of UserType in getter.
I am Linking User table with the Application Access. Here one User can have access to many applications.
I have done the mapping successfully with the below piece of code.
User entity object:
#Entity
#Table(name = "USER_TBL", uniqueConstraints = { #UniqueConstraint(columnNames = "USER_NAME") })
public class User implements Serializable {
.....
#Id
#GeneratedValue
#Column(name = "USER_ID", unique = true, nullable = false)
private Integer userId;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "user")
private List<UserAppAssociation> userAssociatedApplications = new ArrayList<UserAppAssociation>();
Getter and setter for userAssociatedApplications
}
Application access object:
#Entity
#Table(name="APPLICATION_ASSOC_TBL")
public class UserAppAssociation implements Serializable{
#Id
#Column(name="user_id", unique=true, nullable=false)
private Integer userId;
#Column(name = "application_id")
private Integer appId;
#Column(name = "user_type_id")
private Integer userTypeId;
...
#ManyToOne
#JoinColumn(name="USER_ID",insertable=false,updatable=false)
private User user;
..
getters and setters
}
Issue:
I am getting the same values in the Application List ('userAssociatedApplications'). Though i have different values in the application access table, I get the same values in the list. The first row value is repeated in the list.
DB:
I have 'User' table and the mapping is with application access
User table: USER_TBL
Columns
user_id name phone
Application access table : APPLICATION_ASSOC_TBL
Columns
User_id application_id and User_type
Note - no primary key in this table
Sample data:
User_id application_id User_type
1 1 1
1 2 1
1 3 1
Issue: I am getting the first value 1,1,1 in the list thrice.
Expected: List should be with 3 different values
Kindly help. I am not sure whether i am missing anyting in the annotation mapping.
Looks like a problem with this
#Id
#Column(name="user_id", unique=true, nullable=false)
private Integer userId;
#ManyToOne
#JoinColumn(name="USER_ID",insertable=false,updatable=false)
private User user;
Try to use this mapping. Please, refer this as a guide for names and don't use unnecessary annotations
#Entity
#Table(name = "xxx_users", uniqueConstraints = { #UniqueConstraint(columnNames = "f_name") })
public class User {
#Id
#GeneratedValue
#Column(name = "f_id")
private Integer id;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "user")
private List<UserAppAssociation> applications = new ArrayList<UserAppAssociation>();
}
#Entity
#Table(name="xxx_user_applications")
public class UserAppAssociation {
#Id
#GeneratedValue
#Column(name = "f_id")
private Integer id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="fk_user")
private User user;
}