Simple object persist in Spring + hibernate - java

I suppose it is not standard way of doing that so any tips will be helpful, here is my code:
#RequestMapping("/register")
public String register(Map<String, Object> map, #ModelAttribute("user") MyUser user) {
if(user.getLogin() == ""){
map.put("user", new MyUser());
}
else{
map.put("user", user);
map.put("result", userService.addMyUser(user));
}
return "register";
}
what cause following error:
org.hibernate.AssertionFailure: null id in org.mypackage.MyUser entry
(don't flush the Session after an exception occurs)
Here is MyUser class:
#Entity
#Table(name="MyUser")
public class MyUser{
#Id
#Column(name="idMyUser")
#GeneratedValue
private Integer id;
#Column(name="login")
private String login;
#Column(name="password")
private String password;
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}

Try changing the strategy and/or generator for the #GeneratedValue, see here and here for details (for example, you could try #GeneratedValue(strategy = GenerationType.IDENTITY). You could also check if your database table is set to generate the primary key values. The exception seems to indicate that the primary key -field is left unset by the current strategy and/or generator.

Related

Spring Boot: "Column 'username' cannot be null" on #manytoone join column

I'm trying to post the following json file into mysql database in postman.
{
"rem_month": 3,
"rem_day": 23,
"description": "Happy birthday!",
"username": "mortykrox93"
}
But i keep getting the error "Column 'username' cannot be null"
The app is supposed to allow me to login and add multiple reminders for each user.
Here is the sql files the entities are supposed to model:
user.sql
USE `login-reminder`;
CREATE TABLE `user` (
`email_id` varchar(30) DEFAULT NULL,
`password` varchar(30) DEFAULT NULL,
`username` varchar(30) NOT NULL,
PRIMARY KEY(`username`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
reminder.sql
USE `login-reminder`;
CREATE TABLE `reminder` (
`rem_num` int(12) NOT NULL,
`rem_month` int(2) DEFAULT NULL,
`rem_day` int(2) DEFAULT NULL,
`description` varchar(255) DEFAULT NULL,
`username` varchar(30) NOT NULL,
PRIMARY KEY(`rem_num`),
FOREIGN KEY(`username`) REFERENCES user(`username`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;
Here are the two entity files:
User.java
#Entity
#Table(name="user")
public class User {
#Column(name="email_id")
private String emailId;
#Column(name="password")
private String password;
#Id
#Column(name="username")
private String username;
#OneToMany(mappedBy="theUser", cascade = CascadeType.ALL)
private List<Reminder> reminders;
public User() {
}
public User(String emailId, String password, String username) {
this.emailId = emailId;
this.password = password;
this.username = username;
}
public String getEmailId() {
return emailId;
}
public void setEmailId(String emailId) {
this.emailId = emailId;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
Reminder.java
#Entity
#Table(name="reminder")
public class Reminder {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="rem_num")
private int remNum;
#Column(name="rem_month")
private int remMonth;
#Column(name="rem_day")
private int remDay;
#Column(name="description")
private String description;
#ManyToOne
#JoinColumn(name="username")
private User theUser;
public Reminder() {
}
public Reminder(int remNum, int remMonth, int remDay, String description) {
this.remMonth = remMonth;
this.remDay = remDay;
this.description = description;
}
public int getRemNum() {
return remNum;
}
public void setRemNum(int remNum) {
this.remNum = remNum;
}
public int getRemMonth() {
return remMonth;
}
public void setRemMonth(int remMonth) {
this.remMonth = remMonth;
}
public int getRemDay() {
return remDay;
}
public void setRemDay(int remDay) {
this.remDay = remDay;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
Here is the restcontroller.
ReminderController.java
#RestController
public class ReminderController {
#Autowired
private ReminderRepository reminderRepository;
#GetMapping("/reminders")
public List<Reminder> getAllReminders() {
return reminderRepository.findAll();
}
#PostMapping("/reminders")
#CrossOrigin(origins = "http://localhost:4200")
public Reminder createReminder(#RequestBody Reminder reminder) {
return reminderRepository.save(reminder);
}
}
If anyone can help I would appreciate it. Not sure if my entities are matching up with my sql statements, any suggestions would help.
Check your reminder sql, it's username field is set as not null.
The binding is with the User object. So, in your json, it you should send the user like this:
{
"rem_month": 3,
"rem_day": 23,
"description": "Happy birthday!",
"theUser":{
"username":"mortykrox93",
//and other fields if necessary
}
}
This is occurring because of there is no field named username present in Reminder entity class and you are referring to same class in controller with annotation #requestbody to be bind with the request. Actually during deserialization no valid mapping is present for json field named username. so by default username is being set as null because of its datatype string.
Note: It's better to use separate model/pojo class for binding the request. And then map it to proper entity objects.
First, you need change your json that indicates by #user404:
{
"rem_month": 3,
"rem_day": 23,
"description": "Happy birthday!",
"theUser":
{
"username":"mortykrox93",
//and other fields if necessary
}
}
also, the problem is in jackson deserialize, you need to use the anotation for make the relashionship in jackson (is different that Hibernate/JPA) #JsonBackReference and #JsonManagedReference:
In User entity:
#JsonBackReference
#OneToMany(mappedBy="theUser", cascade = CascadeType.ALL)
private List<Reminder> reminders;
In Reminder Entity:
#JsonManagedReference
#ManyToOne
#JoinColumn(name="username")
private User theUser;
Anyway, is recomended use separated DTO classes for transfer data and add in those the jackson annotation. In the entity only use JPA annotations.

DynamoDBMappingException: no RANGE key value present

I am new to NoSQL and Amazon Dynamo DB. I am trying to retreive a user by username first from a DynamoDB UserMaster table.
I have a table UserMaster with 5 attributes(username, correct-hash, email, lastLogin, role), each of type String and I have a corresponding UsermasterBean mapped to the table UserMaster. UserMaster table's Partition Key(Hashkey) is username and Sort key(Range Key) is correct-hash
UsermasterBean
#DynamoDBTable(tableName = "UserMaster")
public class UsermasterBean {
private String username;
private String correctHash;
private String email;
private String lastLogin;
private String role;
#DynamoDBHashKey(attributeName = "username")
#DynamoDBAttribute(attributeName = "username")
public String getUsername() {
return username;
}
#DynamoDBRangeKey(attributeName = "correct-hash")
#DynamoDBAttribute(attributeName = "correct-hash")
public String getCorrectHash() {
return correctHash;
}
#DynamoDBAttribute(attributeName = "email")
public String getEmail() {
return email;
}
#DynamoDBAttribute(attributeName = "last-login")
public String getLastLogin() {
return lastLogin;
}
#DynamoDBAttribute(attributeName = "role")
public String getRole() {
return role;
}
public void setUsername(String username) {
this.username = username;
}
....
....
}
Retrieve data from UI:
UsermasterBean usermasterBean = new UsermasterBean();
UsermasterDao usermasterDao = new UsermasterDao();
usermasterBean.setUsername(username.getValue()); // Get the username from UI form
final String inputtedPassword = password.getValue(); // Get the password from UI form
UsermasterBean retrievedUserBean = usermasterDao.findByUsernameAndPassword(usermasterBean,inputtedPassword);
Validate User:
public UsermasterBean findByUsernameAndPassword(final UsermasterBean usermasterBean, final String inputtedPassword)
throws IOException {
AmazonDynamoDBClientHandler amazonDynamoDBClientHandler = AmazonDynamoDBClientHandler.getNewInstance();
UsermasterBean retrievedUser;
try {
AmazonDynamoDB amazonDynamoDB = amazonDynamoDBClientHandler.createNewClient();
DynamoDBMapper dynamoDBMapper = new DynamoDBMapper(amazonDynamoDB);
retrievedUser = dynamoDBMapper.load(usermasterBean.getClass(), usermasterBean.getUsername());
System.out.println("RETRIEVED CORRECT-HASH FROM DATABASE: " + retrievedUser.getCorrectHash()); // Check if hash retrieved is correct for this user.
// PasswordUtilityManager.verifyPassword(inputtedPassword,retrievedUser.getCorrectHash());
} catch (IOException ioException) {
throw ioException;
} finally {
amazonDynamoDBClientHandler.shutdownClient();
}
return retrievedUser;
}
Problem:
retrievedUser = dynamoDBMapper.load(usermasterBean.getClass(), usermasterBean.getUsername()); throws com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMappingException
I am expecting retrievedUser.getCorrectHash() should display the hashed password stored in database as a String so that I can verify if the inputted password creates the same hash as retrieved from the database.
This exception is being throw because the DynamoDBMapper is expecting the range key to be passed in too (as your DynamoDB table contains a range key of correct-hash).
The correct function call should be as below
retrievedUser = dynamoDBMapper.load(usermasterBean.getClass(), usermasterBean.getUsername(), usermasterBean.getCorrectHash());
As this is presumably the hash for your users password, you can specify a hashed copy of the password provided by the user. If this returns no results in DynamoDB you can assume that either their username or password is incorrect.
As per your data model, there can be more than one entry in the UserMaster table for a single user (username) which is not your intention. (Why would a user have two hashed passwords?)
Do not model correctHash as a range key. If you do, DynamoDB mandates you to provide both the hash and range key when calling load (else you have to query).
See: DynamoDBMapper load vs query

Hibernate #EmbeddedId's behaviour

I was going through some tutorials on Hibernate, and I came across one scenario in which a primary key can be a compound (like 2-3 fields taken together form primary key). As per my understanding, this is achievable using #EmbeddedId annotation.
I tried to do this using simple application.
UserObj Object:
package com.vipin.model;
import javax.persistence.Embeddable;
#Embeddable
public class UserObj {
private int userId;
private String userName;
private String SSN;
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getSSN() {
return SSN;
}
public void setSSN(String sSN) {
SSN = sSN;
}
}
****UPDATE*****
UserModel which uses UserObj as Compound primary key:
#Entity(name="USER_DETAILS")
public class UserModel implements Serializable {
#EmbeddedId
private UserObj userObj;
#Id
#Column(name="USER_ID")
#GeneratedValue(strategy=GenerationType.SEQUENCE)
private int userId;
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public UserObj getUserObj() {
return userObj;
}
public void setUserObj(UserObj userObj) {
this.userObj = userObj;
}
Main application/ demo application:
package com.vipin.mainapplication;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import com.vipin.model.UserModel;
import com.vipin.model.UserObj;
public class HibernateMain {
public static void main(String[] args) {
UserModel userModel = new UserModel();
UserObj userObj = new UserObj(); //Demo of Primary key which is an object.
userObj.setUserId(1);
userObj.setUserName("VIPIN KOUL");
userObj.setSSN("000001");
userModel.setUserObj(userObj); //Demo of Primary key which is an object.
SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
Session session = sessionFactory.openSession();
session.beginTransaction();
session.save(userModel);
session.getTransaction().commit();
session.close();
sessionFactory.close();
}
}
However when I run this program, i am able to save the data into DB, even though i supply the same values for all these fields. With #EmbeddedId, isn't hibernated supposed to check that it doesn't allow to save this object into DB if same object exists in DB (i.e. duplicate values not allowed).
Do we need to enforce anything on DB side as well? I am under the impression that this annotation is going to check.
Any clues greatly appreciated.
***UPDATE****
I missed to include one more piece of code in UserModel:
#Id
#Column(name="USER_ID")
#GeneratedValue(strategy=GenerationType.SEQUENCE)
private int userId;
With this, hibernate was taking the combination of these four coloumns as primary key, and so no exception.
I removed this field and when i run the program second time (with same values), it throw the exception.
Hence, in conclusion #EmbeddedId does enforce primary key in case we have compound primary key.

Models not being saved in Play Framework

Assume a model named User:
#Entity
public class User extends Model {
#Id
#Constraints.Min(10)
public Long id;
#Constraints.Required
public String username;
#Constraints.Required
public String password;
public static Finder<Long, User> find = new Finder<Long, User>(
Long.class, User.class
);
}
When I attempt to update an instance of User in my controller:
User user = User.find.where().eq("username", username).findUnique();
if(user != null) {
user.username = "some_new_username";
user.save();
}
no changes seem to be committed. I read somewhere that when you alter a model instance by its property, it does not get dirty and therefore no changes take place. Hence you should use a setter instead. In the documentation of Play Framework it is said that those setters (and getters) are generated automatically, but using user.setUsername(username) gives me a compilation error:
cannot find symbol [symbol: method setUsername(java.lang.String)] [location: class models.User]
Am I missing something?
Have you tried adding custom setters?
#Entity
public class User extends Model {
#Id
#Constraints.Min(10)
public Long id;
#Constraints.Required
public String username;
public void setUsername(String _username) {
username = _username;
}
#Constraints.Required
public String password;
public void setPassword(String _password) {
password = _password;
}
public static Finder<Long, User> find = new Finder<Long, User>(
Long.class, User.class
);
}
As far as I can tell, automatic getter/setter translation is broken in Play2. Your assignment:
user.username = "some_new_username";
should have triggered the function call:
user.setUsername("some_new_username");
This translation seems to be broken in Play 2. Here's my own question on the subject.

Relationships question in hibernate

I'm learning Hibernate and Play framework (also add Java into account...). I'm having problems saving this kind of entity
#Entity
#Table(name="users")
public class User extends Model {
#Required
public String username;
#Column(name="user_displayname",nullable=true)
public String displayname;
#Password
public String user_password;
#Email
#Column(name="user_email",nullable=false,unique=true)
public String user_email;
public String user_salt;
public Date user_joindate;
#ManyToOne
#JoinTable(name="users_meta")
public UserMeta userdata;
#Required
public boolean user_isActive;
#OneToOne(targetEntity=UserPhotos.class,cascade=CascadeType.ALL)
#JoinColumn(name="id",referencedColumnName="userID")
public UserPhotos userPhoto;
#ManyToMany(cascade=CascadeType.ALL)
#JoinTable(name="links_rol2user")
public List<Rol> rol;
public User (String username, models.Pass password, String user_email) {
this.username = username;
this.user_password = password.getHashedPassword();
this.user_salt = password.getUserHash();
this.user_email = user_email;
this.user_joindate = new Date();
this.user_isActive = false;
}
This is my code when I'm registering a user
// check if the validation has errors
if(validation.hasErrors()) {
params.flash(); // add http parameters to the flash scope
validation.keep(); // keep the errors for the next request
register();
} else {
Cache.delete(uuid);
Pass pass = new Pass(password,new Date().toString());
User newUser = new User(firstName, pass, email);
UserMeta utest = new UserMeta(newUser.id);
utest.setUserTownID(pueblos);
newUser.setUserMeta(utest);
newUser.save();
Logger.info("NewUser ID : %s", newUser.getId());
// UserMeta userInfo = new UserMeta(newUser.getId());
// userInfo.setUserTownID(pueblos);
// userInfo.save();
// TODO salvar foto a null
// Confirmation left
Cache.set("thankyou", "alright!", "3mn");
thankyou();
}
I'm trying to save the userMeta, it does creates a new record when I set the userMeta object into newUser (not visible right now), but it doesn't insert the new ID created in newUser.
What kind of relation do I need? before I tweaked the code as it is now, it was a OneToOne relationship, worked quite well, but now when I was completing the register functions it kinda hit me that I needed to save userMeta object too..
If you need more info let me know, I don't know if I explained it well or not, just trying to get the hang of how Hibernate do relations, etc.
Adding UserMeta:
*/
#Entity
#Table(name="users_meta")
public class UserMeta extends Model {
#Lob
#Column(name="userBio")
public String userBio;
#Column(name="userPhotoID",nullable=true)
public Long userPhotoID = null;
#Column(name="userRoleID", nullable=false)
public Long userRoleID = 2L;
#Lob
public String userDescription;
#Column(name="userViews", nullable=false)
public Long userViews = 0L;
#Column(name="userFavoriteCount", nullable=false)
public Long userFavoriteCount = 0L;
#Column(name="userTotalComments", nullable=false)
public Long userTotalComments = 0L;
#Column(name="userTotalUploadedVideos", nullable=false)
public Long userTotalUploadedVideos = 0L;
public Long userTownID;
public Long userID;
public UserMeta() {}
public UserMeta(Long userid) {
this.userBio = "El usuario no ha escrito nada todavia!";
this.userDescription = "El usuario todavia no se ha describido!";
this.userID = userid;
}
public Long getUserTownID() {
return userTownID;
}
public void setUserTownID(Long userTownID) {
this.userTownID = userTownID;
}
}
// pass model
public class Pass {
protected String hashed;
protected String userHash;
public Pass(String passwordToHash, String salt) {
StringBuffer passSalt = new StringBuffer(passwordToHash);
this.userHash = DigestUtils.md5Hex(salt);
passSalt.append(this.userHash);
passSalt.append(Play.configuration.getProperty("application.passwordSalt"));
this.hashed = DigestUtils.sha512Hex(passSalt.toString());
}
public String getHashedPassword() {
return this.hashed;
}
public String getUserHash() {
return this.userHash;
}
}
There seems to be a lot going on there! But from what I can tell, you problem is with the id that you are passing into the UserMeta.
As you are extending Model, the id is being generated by the Model class. However, this is not set until after the entity is saved to the database (as the id is auto-generated by the database).
Therefore, because you are passing the id into the UserMeta before the User object is saved, the value of id will be null.
If you can save the User object before you create your UserMeta object, your code should work.

Categories

Resources