DynamoDBMappingException: no RANGE key value present - java

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

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.

Datastore Filter doesn't work in Google App Engine with java

I have a problem with retrieving data from data store.
Here is a sample of an Entity stored on the data store:
I am using GAE with java, and I managed to store data in the data store, but now I need to fetch the stored data using DatastoreService() and Filter().
The problem is that even when I enter the right values in the WHERE clause, there is no data !!
DatastoreService dataStore = DatastoreServiceFactory.getDatastoreService();
Filter usernameFilter = new FilterPredicate("Name", FilterOperator.EQUAL, "moha");
Query q = new Query("User").setFilter(usernameFilter);
PreparedQuery pq = dataStore.prepare(q);
pq.countEntities() //returns 0 always
I have even tried to use the online "Query by GQL" and still no data:
select * from User where Name = 'moha'
Here is the entity code:
#Entity
public class User {
#Id private String username;
private String password;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
What should I do?
The ID/Name you see in the datastore viewer is in the Key and is not a property of the entity such as 'Name'.
You should include you model and how you created the entity.
Your select query will be for key not Name.
The simplest way to prove this is get the enrity by it's key which you can see above.

Map two ArrayList with different sizes

For Example:
ArrayList1 = {userid1, userid2, userid1, userid4, userid1, userid3, userid2, userid4, userid4, userid4, userid2};
ArrayList2 = {username1, username2, username3, username4};
Mapping these two array so that whenever I call ArrayList1.get(0).getUserName(), it should provide me with username1.
public class User {
String username;
public User(String username)
{
this.username = username;
}
/**
* #return the username
*/
public String getUsername() {
return username;
}
/**
* #param username the username to set
*/
public void setUsername(String username) {
this.username = username;
}
}
userid1, userid2... must be User objects
User userid1 = new User("username1");
User userid2 = new User("username2");
Initialize all the user objects
ArrayList1 = {userid1, userid2, userid1, userid4, userid1, userid3, userid2, userid4, userid4, userid4, userid2};
Then you can call
String username = ArrayList1.get(0).getUserName();
this will return username1
There is a better way to do that and that is by using HashMap:
//create your custom object which will be mapped
public class User{
public String userId;
public String userName;
}
ArrayList<String> userKeys = new ArrayList<String>();
HashMap<String, User> users = new HashMap<String, User>();
Now using a userKey, you can access its corresponding userData;
Example:
User user = users.get("yourKey");
I think you should use :
List<List<T>> = ArrayList<ArrayList<T>>;
T is the class of you UserName.
Use HashSet instead of arraylist. Set does not allow duplicate.

Enforce unique field in appengine datastore (java)

I have a User class with a String field emailAddress, on which I want to enforce uniqueness. I was having difficulty getting the available suggested solutions to work, so I wrote the following process which seems to work, using a separate class with the unique field as its primary key.
Whilst it seems to work, it smells because I'm intentionally creating an exception when nothing incorrect happens (by trying to retrieve a non-existent EmailAddress object).
Can anyone comment on whether this method will work reliably, in particular across multiple appengine instances.
Thanks.
Firstly, I have the User class:
#PersistenceCapable
public class User {
public User(String emailAddress, String otherData) {
this.emailAddress = emailAddress;
this.otherData = otherData;
}
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
#Persistent
private String emailAddress;
#Persistent
private String otherData;
}
I then have an EmailAddress class, using the email address as the primary key:
#PersistenceCapable
public class EmailAddress {
public EmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
}
#PrimaryKey
#Persistent
private String emailAddress;
}
Usage of these classes is then as follows:
public class UserManager {
public User createUser(String email, String otherData) {
User user = new User(emailAddress, otherData);
EmailAddress emailAddress = new EmailAddress(email);
PersistenceManager pm = PMF.get().getPersistenceManager();
Transaction tx = pm.currentTransaction();
User persistedUser = null;
try {
tx.begin();
EmailAddress testEmailAddress = null;
try {
testEmailAddress = pm.getObjectById(EmailAddress.class, email);
} catch (Exception e) {
//
}
if (null != testEmailAddress) {
throw new RuntimeException("That email address already has an account");
}
pm.makePersistent(emailAddress);
persistedUser = pm.makePersistent(user);
txn.commit();
return persistedUser;
} finally {
if (txn.isActive()) {
txn.rollback();
}
pm.close();
}
}
...
}

Simple object persist in Spring + hibernate

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.

Categories

Resources