Models not being saved in Play Framework - java

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.

Related

Hibernate: Getting result using a specific field

I've been following a lot of tutorial on how to get a list of result by referencing a specific column in the table.
I have this table.
I want to get the list of result with a plan_code "TEST123"
This is my code:
PlanRepository.java
public interface PlanCoverageRepository extends CrudRepository<PlanCoverage, Long> {
List<PlanCoverage> findAllByPlan_code(String plan_code);
}
PlanCoverageService.java
public interface PlanCoverageService {
public List<PlanCoverage> getAllPlanCoverageByPlanCode(String plan_code);
}
PlanCoverageServiceImpl.java
#Service
#Transactional
public class PlanCoverageServiceImpl implements PlanCoverageService {
#Override
public List<PlanCoverage> getAllPlanCoverageByPlanCode(String plan_code) {
return (List<PlanCoverage>) planCoverageRepository.findAllByPlan_code(plan_code);
}
}
PlanCoverageController.java
#Controller
#RequestMapping(value="/admin")
public class PlanCoverageController {
#Autowired
PlanCoverageService planCoverageService;
#RequestMapping(value="/Test/{plan_code}", method=RequestMethod.GET)
public ModelAndView test(#PathVariable String plan_code) {
ModelAndView model = new ModelAndView();
PlanCoverage planCoverage = (PlanCoverage) planCoverageService.getAllPlanCoverageByPlanCode(plan_code);
model.addObject("planCoverageForm",planCoverage);
model.setViewName("plan_coverage_form");
return model;
}
}
PlanCoverage.java
#Entity
#Table(name="plan_coverage")
public class PlanCoverage {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private long coverage_id;
#Column(name="plan_code")
private String plan_code;
#Column(name="coverage_description")
private String coverage_description;
/..getters and setters
#ManyToOne()
#JoinColumn(name="plan_code", referencedColumnName = "plan_code",insertable=false, updatable=false)
private Plan plan;
public Plan getPlan() {
return plan;
}
public void setPlan(Plan plan) {
this.plan = plan;
}
}
Please help me. I've been stuck with these for a few days and non of the tutorials seems to work on me. Thank you so much!!
You have messed up with the convention that spring boot is using to compose query methods. The case of the fields in the entity should follow the lower camel-case scheme, like so:
#Column(name="plan_code")
private String planCode;
and then the query method in PlanCoverageRepository should be:
List<PlanCoverage> findAllByPlanCode(String planCode);

Spring Data JPA Unable to locate Attribute with the given name

I was trying to use Spring Data JPA on Spring Boot and I kept getting error, I can't figure out what the problem is:
Unable to locate Attribute with the the given name [firstName] on
this ManagedType [com.example.h2demo.domain.Subscriber]
FirstName is declared in my entity class. I have used a service class with DAO before with different project and worked perfectly.
My Entity class (getters and setters are also in the class) :
#Entity
public class Subscriber {
#Id #GeneratedValue
private long id;
private String FirstName,LastName,Email;
public Subscriber(long id, String firstName, String lastName, String email) {
this.id = id;
this.FirstName = firstName;
this.LastName = lastName;
this.Email = email;
}
}
...
My Repository Class
#Component
public interface SubscriberRepository extends JpaRepository<Subscriber,Long> {
Subscriber findByFirstName(String FirstName);
Subscriber deleteAllByFirstName(String FirstName);
}
My Service Class
#Service
public class SubscriberService {
#Autowired
private SubscriberRepository subscriberRepository;
public Subscriber findByFirstName(String name){
return subscriberRepository.findByFirstName(name);
}
public Subscriber deleteAllByFirstName(String name){
return subscriberRepository.deleteAllByFirstName(name);
}
public void addSubscriber(Subscriber student) {
subscriberRepository.save(student);
}
}
And My Controller class:
#RestController
#RequestMapping("/subscribers")
public class SubscriberController {
#Autowired
private SubscriberService subscriberService;
#GetMapping(value = "/{name}")
public Subscriber findByFirstName(#PathVariable("name") String fname){
return subscriberService.findByFirstName(fname);
}
#PostMapping( value = "/add")
public String insertStudent(#RequestBody final Subscriber subscriber){
subscriberService.addSubscriber(subscriber);
return "Done";
}
}
Try changing private String FirstName,LastName,Email; to private String firstName,lastName,email;
It should work.
findByFirstName in SubscriberRepository tries to find a field firstName by convention which is not there.
Further reference on how properties inside the entities are traversed https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods.query-property-expressions
The same problem was when i had deal with Spring Data Specifications (https://www.baeldung.com/rest-api-search-language-spring-data-specifications)
Initial piece of code was:
private Specification<Project> checkCriteriaByProjectNumberLike(projectNumber: String) {
(root, query, criteriaBuilder) -> criteriaBuilder.like(root.get("project_number"), "%" + projectNumber)
}
The problem was in root.get("project_number"). Inside the method, I had to put the field name as in the model (projectNumber), but I sent the field name as in the database (project_number).
That is, the final correct decision was:
private Specification<Project> checkCriteriaByProjectNumberLike(projectNumber: String) {
(root, query, criteriaBuilder) -> criteriaBuilder.like(root.get("projectNumber"), "%" + projectNumber)
}
After I change my entity class variables from capital letter to small letter for instance Username to username the method Users findByUsername(String username); is working for me now .
As per specification , the property names should start with small case.
...The resolution algorithm starts with interpreting the entire part (AddressZipCode) as the property and checks the domain class for a property with that name (uncapitalized)....
It will try to find a property with uncapitalized name. So use firstName instead of FristName and etc..

Java Class Relationship and ORM

I have this simple login-registration functionality using the following classes.
User class:
public class User implements Serializable {
private int userId;
private String username;
private String password;
public User() {
userId = 0;
username = "";
password = "";
}
// public set-get methods here
}
Verification class:
public class Verification implements Serializable {
private int userId;
private String code;
private long expirationDate; // millis
public Verification() {
userId = 0;
code = "";
expirationDate = 0;
}
public void setUserId(int Id) {
userId = Id;
}
public void setUserId(User user) {
userId = user.getUserId();
}
// the rest of set-get methods here
}
I would like to confirm if the relationship above is considered Dependency? or Association?
Also, I'm using this classes in ORM. Does omitting one setUserId() overloaded method mess up the relationship between(if there's any)? Thank you.
I would say a dependency exists between Verification and User, as you can pass a User reference to a method of Verification (it uses that reference and depends on it), but Verification instance does not OWN a reference to a User instance, instead it consumes one.
The example you gave , describes dependency .
For reference you can visit the link given below -:
https://nirajrules.wordpress.com/2011/07/15/association-vs-dependency-vs-aggregation-vs-composition/

How can I create a custom setter for setting objects in Jersey/Jackson?

I have an entity called Checkout which has a user and an item. So when a user wants to create a new checkout object, they POST to "/checkout". Now, I was thinking to set the user and item, the user would instead include a username and a serial number, like this for example:
{
"id": "1",
"user": "hassan",
"item": "sdf2ljt234jt09jsd"
}
Then I would just write custom setters in my Checkout class that take Strings rather than Users and Items:
public void setUser(String username) {
this.user = ...
}
But Jersey is never calling my setter. Both the user and the item properties remain null, and I'm not sure why. If this isn't the proper way to set objects with Jersey/Jackson, is there something else I should be trying?
You can use #JsonDeserialize to specify a builder class that can hold the custom logic for going from a String to a User or Item:
#JsonDeserialize(builder = Checkout.Builder.class)
public class Checkout {
private Long id;
private User user;
private Item item;
// getters and setters
public static class Builder {
private Long id;
private String username;
private String serialNumber;
#JsonProperty("id")
public Builder setId(Long id) {
this.id = id;
return this;
}
#JsonProperty("user")
public Builder setUsername(String username) {
this.username = username;
return this;
}
#JsonProperty("item")
public Builder setSerialNumber(String serialNumber) {
this.serialNumber = serialNumber;
return this;
}
public Checkout build() {
Checkout checkout = new Checkout();
checkout.setId(id);
checkout.setUser(/* lookup user by username */);
checkout.setItem(/* lookup item by serialNumber */);
return checkout;
}
}
}
Notice the use of #JsonProperty on each of the setters of the Builder class and how they use the names of the properties in your JSON. That's important to make sure your JSON structure maps properly into the builder fields.

JPA/Hibernate bidirectional many-to-one results in StackOverflowException

I have entities User and GrantedRole that have a bidirectional one-to-many relation.
When I try to add a GrantedRole to the Set in User, there is no exception thrown, but when I debug the variables for the User and GrantedRole object have a description that reads
com.sun.jdi.InvocationException occurred invoking method.
The different fields for the variables can be read while debugging, but when I select the roles field in User or the user field in GrantedRole I get the same description as above.
When I go into the Set of GrantedRole in user, I eventually find the following description:
Detail formatter error:
An exception occurred: java.lang.StackOverflowError
My code:
public class User {
private Set<GrantedRole> roles = new HashSet<GrantedRole>();
public User() {
super();
}
public User(String name, String password) {
this.name = name;
this.password = password;
}
#OneToMany(fetch = FetchType.EAGER, mappedBy = "user")
public Set<GrantedRole> getRoles() {
return roles;
}
public void setRoles(Set<GrantedRole> roles) {
this.roles = roles;
}
// equals and hashCode are based on username
// toString is based on all fields
}
public class GrantedRole {
private user user;
public GrantedRole() {
super();
}
public GrantedRole(User user, Role role, Organization organization) {
this.user = user;
this.role = role;
this.organization = organization;
}
#ManyToOne
#NotNull
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
// equals and hashCode are based on all fields
// toString was based on all fields, I've changed it to not include User.
}
public class Test {
public void testStuff() {
User user = new User("name");
GrantedRole role = new GrantedRole(user, "role"); //this sets the user field
user.getRoles().add(role); //doesn't throw Exception, but debugging shows InvocationTargetException and StackOverflowException
System.out.println(user.getRoles()); //throws StackOverflowException, as I would expect
}
}
It was my understanding that I should be able to set up a bidirectional relation in this way. I read multiple tutorials, like this one, and I donĀ“t see what I am doing differently to cause the .add(role) to go wrong.
I left out some trivial code, if it is needed I will gladly provide it. I haven't made code to ensure that a GrantedRole with a reference to a User is referenced by that user in return, but I think that is not relevant for the problem I'm having.
So, I was just being stupid and had a recursive call in the toString() methods.
User.toString() tried to print the roles, and GrantedRole.toString() tried to print the user.
I fixed it by altering the GrantedRole.toString() to print user.getUsername(), thus breaking the cycle.

Categories

Resources