OneToOne relationship hibernate+spring-data-jpa null value in owning schema - java

I am new to Hibernate and JPA (I worked mostly with stored procedure integration using JDBC.). I created two entities User and UserPassword with OneToOne relationship. I am trying to store values in both the tables (MySQL DB) but UserId (foreign_key) column of the UserPassword table stores null whereas the password gets stored. Please correct my mistake in below code :
#Entity
#Table(name = "User")
public class User implements Serializable{
private static final long serialVersionUID = -3366411610525655274L;
#Column(name = "UserId", nullable = false,unique = true)
#GenericGenerator(name = "uuid2", strategy = "uuid2")
#GeneratedValue(generator = "uuid2")
#Id
#Type(type="uuid-char")
private UUID userId;
#Embedded
private Name name;
#Column(name = "DateOfBirth", nullable = false)
private Date dob;
#OneToOne(mappedBy="user", cascade=CascadeType.ALL)
private Password password;
public Password getPassword() {
return password;
}
public void setPassword(Password password) {
this.password = password;
}
public UUID getUserId() {
return userId;
}
public void setUserId(UUID userId) {
this.userId = userId;
}
public Name getName() {
return name;
}
public void setName(Name name) {
this.name = name;
}
public Date getDob() {
return dob;
}
public void setDob(Date dob) {
this.dob = dob;
}
}
and
#Entity
#Table(name= "UserPassword")
public class Password implements Serializable{
private static final long serialVersionUID = -8990341903052492314L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="PasswordId")
private Long Id;
#Column(name="Password")
private String password;
#OneToOne(cascade=CascadeType.ALL,fetch=FetchType.LAZY)
#JoinColumn(name="UserId", referencedColumnName="UserId")
private User user;
public Long getId() {
return Id;
}
public void setId(Long id) {
Id = id;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
These are the my JPA repositories :
public interface UserRepository extends JpaRepository<User, UUID>{
}
and
public interface PasswordRepository extends JpaRepository<Password, Long>{
}
and service layer code to save entities in database :
public void insertUsers(List<User> users) {
List<com.poc.entity.User> usersData = ObjectMapperUtils.mapAll(users, com.poc.entity.User.class);
userRepository.saveAll(usersData);
}
Also, please help me in proper designing approach for this work.

It worked by doing small modification in service layer logic.
public void insertUsers(List<User> users) {
List<com.poc.entity.User> usersData = ObjectMapperUtils.mapAll(users, com.poc.entity.User.class);
usersData = usersData.stream().map(user->mapUserPassWordEntity(user)).collect(Collectors.toList());
userRepository.saveAll(usersData);
}
private com.poc.entity.User mapUserPassWordEntity(com.poc.entity.User user) {
Password password = new Password();
password.setPassword(user.getPassword().getPassword());
//set parent reference to child
password.setUser(user);
// set child reference to parent
user.setPassword(password);
return user;
}
Still, I would appreciate more suggestions for better approach.

Related

Native Query mapstruct

I am using native query and I get a Tuple with a set of login, email, pass, regDate.
I also created a class with these attributes (This is not an Entity and I don't need it).
Question: How can I get this class using mapstruct (DTO)
#Query("SELECT * FROM users .......", nativeQuery = true)
List<Tuple> getInfo();
#Data
public class UserPro {
String login;
String email;
String pass;
Date regDate;
}
You are almost there but you have some misunderstood concept. If i understood what you are asking...
The "getInfo" query has to obtain every "user" it receives from the query. Everything it receives it's been saved into a "Tuple" List, I suppose Tuple is a model or dto. If you want to save what you receive from that query you have to create a model, not a dto and you may call it "user".
Dtos are classes made to work with data relationated to a Http request, not a query.
Models are classes made to work with data relationated to a database, including database queries.
Inside that "user" model you have to instantiate every column you get from the database (be sure to name the #Column annotation variable the same each column is named on the database and give them the correct data type as in the Database).
Keep in mind that every user has an unique identifier, it doesn't have to be called ID, but you have to know who that unique identifier is.
After instantiating every variable you have to instantiate the getters and setters and a constructor instantiating every variable.
Example:
#Entity
#Table(name = "TABLE_NAME")
public class User implements Serializable
{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "PUT_HERE_YOUR_UNIQUE_INDENTIFIER_HERE")
private "UNIQUE_INDENTIFIER_DATA_TYPE" id;
#Column(name = "LOGIN_COLUMN_NAME")
private String login;
#Column(name = "EMAIL_COLUMN_NAME")
private String email;
#Column(name = "PASSWORD_COLUMN_NAME")
private String pass;
#Column(name = "REGDATE_COLUMN_NAME", nullable = false)
private Date regDate;
//default constructor
public User() {}
//user constructor
public User()
{
this.id = id;
this.login = login;
this.email = email;
this.pass = pass;
this.regDate = regDate;
}
public Long getId()
{
return id;
}
public void setId(Long id)
{
this.id = id;
}
public String getLogin()
{
return login;
}
public void setLogin(String login)
{
this.login = login;
}
public String getEmail()
{
return email;
}
public void setEmail(String email)
{
this.email = email;
}
public String getPass()
{
return pass;
}
public void setPass(String pass)
{
this.pass = pass;
}
public Date getRegDate()
{
return regDate;
}
public void setRegDate(Date regDate)
{
this.regDate = regDate;
}
}

JPA system exception error accesing field

I'm trying to implement a unidirectional many to many relationship between entities with spring+JPA.
After a few tries changing hibernate versions I don't know whats the cause
Caused by: org.springframework.orm.jpa.JpaSystemException: Error accessing field [private java.lang.Integer com.uca.refactor2.model.Achievement.id] by reflection for persistent property [com.uca.refactor2.model.Achievement#id] : 1; nested exception is org.hibernate.property.access.spi.PropertyAccessException: Error accessing field [private java.lang.Integer com.uca.refactor2.model.Achievement.id] by reflection for persistent property [com.uca.refactor2.model.Achievement#id] : 1
User.java
#Entity
#Table(name="USER")
public class User implements Serializable {
private static final long serialVersionUID = 4402583037980335445L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String firstName;
private String lastName;
#Column(unique = true)
private String username;
private String password;
#Enumerated(EnumType.STRING)
private UserType userType;
#OneToMany(cascade=CascadeType.ALL, mappedBy="joinedUserAchievementId.user")
private List<JoinedUserAchievement> joinedUserAchievementList = new ArrayList<JoinedUserAchievement>();
public User() {}
public User(Integer id) {
this.id = id;
}
public User(String username, String firstName, String lastName,
String password, UserType userType) {
this.username = username;
this.firstName = firstName;
this.lastName = lastName;
this.password = password;
this.userType = userType;
}
public List<JoinedUserAchievement> getAllAchievement() {
return joinedUserAchievementList;
}
public void addAchievement(Achievement achievement) {
// Notice a JoinedUserAchievement object
Date dateOfAcquisition = new Date();
JoinedUserAchievement joinedUserAchievement = new JoinedUserAchievement(new JoinedUserAchievement.JoinedUserAchievementId(this, achievement),dateOfAcquisition );
joinedUserAchievement.setAchievementId(achievement.getId());
joinedUserAchievementList.add(joinedUserAchievement);
}
//set and gets
JoinedUserAchievement.java
#Entity
#Table(name="USER_ACHIEVEMENT")
public class JoinedUserAchievement {
public JoinedUserAchievement() {}
public JoinedUserAchievement(JoinedUserAchievementId joinedUserAchievementId, Date dateOfAcquisition) {
this.joinedUserAchievementId = joinedUserAchievementId;
this.dateOfAcquisition = dateOfAcquisition;
}
#ManyToOne(targetEntity = Achievement.class)
#JoinColumn(name="id", insertable=false, updatable=false)
private Integer achievementId;
private Date dateOfAcquisition;
public String getDate() {
DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
Date date = dateOfAcquisition;
return dateFormat.format(date);
}
public Integer getAchievementId() {
return achievementId;
}
public void setAchievementId(Integer achievementId) {
this.achievementId = achievementId;
}
#EmbeddedId
private JoinedUserAchievementId joinedUserAchievementId;
// required because JoinedUserAchievments contains composite id
#Embeddable
public static class JoinedUserAchievementId implements Serializable {
/**
*
*/
private static final long serialVersionUID = -9180674903145773104L;
#ManyToOne
#JoinColumn(name="USER_ID")
private User user;
#ManyToOne
#JoinColumn(name="ACHIEVEMENT_ID")
private Achievement achievement;
// required no arg constructor
public JoinedUserAchievementId() {}
public JoinedUserAchievementId(User user, Achievement achievement) {
this.user = user;
this.achievement = achievement;
}
public JoinedUserAchievementId(Integer userId, Integer achievementId) {
this(new User(userId), new Achievement(achievementId));
}
public User getUser() {
return user;
}
public Achievement getAchievement() {
return achievement;
}
public void setUser(User user) {
this.user = user;
}
public void setAchievement(Achievement achievement) {
this.achievement = achievement;
}
#Override
public boolean equals(Object instance) {
if (instance == null)
return false;
if (!(instance instanceof JoinedUserAchievementId))
return false;
final JoinedUserAchievementId other = (JoinedUserAchievementId) instance;
if (!(user.getId().equals(other.getUser().getId())))
return false;
if (!(achievement.getId().equals(other.getAchievement().getId())))
return false;
return true;
}
#Override
public int hashCode() {
int hash = 7;
hash = 47 * hash + (this.user != null ? this.user.hashCode() : 0);
hash = 47 * hash + (this.achievement != null ? this.achievement.hashCode() : 0);
return hash;
}
}
}
Achievement.java
#Entity
#Table(name="ACHIEVEMENT")
public class Achievement implements Serializable{
private static final long serialVersionUID = 7747630789725422177L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
private Integer points;
public Achievement() {
}
public Achievement(String name, Integer points) {
this.name = name;
this.points = points;
}
public Achievement(Integer id) {
this.id = id;
}
//set and gets
I've also tried to make this relationship bidirectional and it didn't work, so I may be missing something
Also before this I had achievement objects instead of achievementId on joinedUserAchievement, it worked but I think its not what I need, I don't need to get every achievement object always, with only the id is fine.
From the docs:
Relationship mappings defined within an embedded id class are not supported
You should put the ids only in JoinedUserAchievementId, and put User and Achievement associations in JoinedUserAchievement directly like so:
public class JoinedUserAchievementId {
private Long userId;
private Long achievementId;
...
}
public class JoinedUserAchievement {
#EmbeddedId
private JoinedUserAchievementId joinedUserAchievementId;
#ManyToOne
#MapsId("userId")
#JoinColumn(name = "USER_ID")
private User user;
#ManyToOne(optional = false, fetch = LAZY)
#MapsId("achievementId")
#JoinColumn(name = "ACHIEVEMENT_ID")
private Achievement achievement;
//if you absolutely need to map the achievement_id column here as well
//note that it will already be mapped to joinedUserAchievementId.achievementId
#Column(name = "ACHIEVEMENT_ID", insertable=false, updatable=false)
private Long achievementId;
...
}
Remember to update the User.joinedUserAchievementList mapping to mappedBy="user".

Oracle table Sequence generator is not working with JPA Composite primary key

My domain classes:
#Entity
#Table(name="ZZ_USER_INFO", schema="PDS_OWNER")
public class User implements Serializable{
private static final long serialVersionUID = 1317056412866001134L;
public User(){
}
#EmbeddedId
private UserPk userPk;
#Column(name="AGE", nullable=false)
private int userAge;
public UserPk getUserPk() {
return userPk;
}
public void setUserPk(UserPk userPk) {
this.userPk = userPk;
}
public int getUserAge() {
return userAge;
}
public void setUserAge(int userAge) {
this.userAge = userAge;
}
}
public class UserPk implements Serializable{
private static final long serialVersionUID = 237150872186063319L;
public UserPk(){
}
#SequenceGenerator(name="ZZ_USER_INFO_SEQ", sequenceName="ZZ_USER_INFO_SEQ")
#GeneratedValue(strategy = GenerationType.AUTO, generator="ZZ_USER_INFO_SEQ")
#Column(name="USER_ID", nullable=false)
private long userId;
#Column(name="USER_NAME", nullable=false)
private String userName;
public long getUserId() {
return userId;
}
public void setUserId(long userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
Here is my test method:
#Test
public void testCreatUser_success(){
User user = new User();
UserPk userPk = new UserPk();
userPk.setUserName("User2");
user.setUserPk(userPk);
user.setUserAge(30);
userRepository.save(user);
}
When i use #SequenceGenerator with in an #EmbeddedId, it is not working in Oracle. In the table, i see the default value in USER_ID column which is Zero.
The same code works fine when I use #Id instead.
Can someone please help me on how to use Oracle sequecne generator with JPA #EmbeddedId ( Composite key)
Thanks for the assitance.

Declaring class is not found in the inheritance state hierarchy: UserAccount

I'm using servlets with JPA+Hibernate). I don't understand the error, unless I've tried other solutions suggested in this forum. In fact, I don't want to store the UserAccount class as an entity; but I just want to declare it in the Utilisateur class (the Ids of the Utilisateur class are declared in the useraccount class).
My code :
#Entity
#Table(name = "utilisateur")
public class Utilisateur implements Serializable {
#Id
private UserAccount userAccount;
private Civility civility;
private Address address;
private Contact contact;
#Column(name = "sessions")
private List<String> sessions;
#Column(name = "particularRules")
private boolean particularRules;
public Utilisateur(UserAccount pAccount, Civility pCivility,
Address pAddress, Contact pContact, List<String>
pSessions,
boolean particularRules) {
this.userAccount = pAccount;
this.civility = pCivility;
this.address = pAddress;
this.contact = pContact;
this.sessions = pSessions;
this.particularRules = particularRules;
}
public Civility getCivility() {
return civility;
}
public void setCivility(Civility civility) {
this.civility = civility;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public Contact getContact() {
return contact;
}
public void setContact(Contact contact) {
this.contact = contact;
}
public boolean isParticularRules() {
return particularRules;
}
public void setParticularRules(boolean particularRules) {
this.particularRules = particularRules;
}
public UserAccount getUserAccount() {
return userAccount;
}
public void setUserAccount(UserAccount userAccount) {
this.userAccount = userAccount;
}
public List<String> getSessions() {
return sessions;
}
public void setSessions(List<String> sessions) {
this.sessions = sessions;
}
}
#Embeddable
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class UserAccount implements Serializable {
public UserAccount() {
}
public UserAccount(String pId, String pEmail, String pwsd, Date pCreaDate, Date pLastModDate) {
this.identifier = pId;
this.email = pEmail;
this.password = pwsd;
this.creationDate = pCreaDate;
this.lastModificationDate = pLastModDate;
}
#OneToOne(mappedBy = "userAccount", cascade = CascadeType.ALL,
fetch = FetchType.EAGER, orphanRemoval = true, targetEntity =
Utilisateur.class)
private Utilisateur user;
#Column(name = "creationDate")
#Temporal(javax.persistence.TemporalType.DATE)
private Date creationDate;
#Column(name = "lastModificationDate")
#Temporal(javax.persistence.TemporalType.DATE)
private Date lastModificationDate;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "identifier", nullable = false)
private String identifier;
#Column(name = "email")
private String email;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "password", nullable = false)
private String password;
public String getIdentifier() {
return identifier;
}
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Date getCreationDate() {
return creationDate;
}
public void setCreationDate(Date creationDate) {
this.creationDate = creationDate;
}
public Date getLastModificationDate() {
return lastModificationDate;
}
public void setLastModificationDate(Date lastModificationDate) {
this.lastModificationDate = lastModificationDate;
}
public Utilisateur getUser() {
return user;
}
public void setUser(Utilisateur user) {
this.user = user;
}
}
You must use an Embedded Primary Key.
See the answer to this question here in Stackoverflow How to create and handle composite primary key in JPA.
Best regards!
It may occur when Embedded Primary Key contains #EmbeddedId. Sub-composite key should be annotated with #Embedded instead.

JPA: #SqlResultSetMapping gives me two objects, not one

I am trying to do some NativeQueries, but I am stuck with #SqlResultSetMapping configuration.
I have these classes:
#Entity
public class LocalUser implements Serializable {
private static final long serialVersionUID = 1265845L;
#Id
private String username;
#Column(nullable=false)
private String password;
#OneToMany
private List<RoleName> roles;
public LocalUser() {
}
public LocalUser(String username, String password, List<RoleName> roles) {
this.username = username;
this.password = password;
this.roles = roles;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public List<RoleName> getRoles() {
return roles;
}
public void setRoles(List<RoleName> roles) {
this.roles = roles;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
#Entity
public class RoleName implements Serializable {
private static final long serialVersionUID = 12598741564894L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String roleName;
public RoleName() {
}
public RoleName(Long id, String roleName) {
this.id = id;
this.roleName = roleName;
}
public RoleName(String roleName) {
this.roleName = roleName;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
}
My question is, how to write propper #SqlResultSetMapping for class LocalUser. My test SQL statement is:
select 'user' as username, 'passwd' as password, 1 as id, 'admin' as roleName;
And I would like to get one Entity Local user, with username user, password passwd and a list of roles, where is admin with id 1.
I figured as far something like this:
#SqlResultSetMapping(name="LocalUserMapping",
entities={
#EntityResult(entityClass=LocalUser.class,fields={
#FieldResult(name="username",column="username"),
#FieldResult(name="password",column="password")
}),
#EntityResult(entityClass=RoleName.class,fields={
#FieldResult(name="id",column="id"),
#FieldResult(name="roleName",column="roleName")
})
})
But it gives me two objects, instead of one. Can anybody help me please? Thanks.
The two objects are resulting from the use of the entityClass= annotations (2x, one for LocalUser and one for RoleName). Combine them into one entityClass definition and one object will be returned.

Categories