Spring JPA with boolean fields throws "cannot resolve property exception" - java

I am facing, "could not resolve property isManager of" Hibernate query exception.
I have the following java class:
public class Employee implements Serializable {
#Column(name = "employee_id")
private Integer employeeId;
#Column(name = "name")
private String name;
#Column(name = "is_manager")
private boolean manager;
public Integer getEmployeeId() {
return employeeId;
}
public void setEmployeeId(Integer employeeId) {
this.employeeId = employeeId;
}
public String getName(){
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isManager(){
return employeeId;
}
public void setManager(boolean manager) {
this.manager = manager;
}
These are the eclipse generated setters and getters. When I replaced,
public boolean isManager() {
return employeeId;
} with
public boolean getManager() {
return employeeId;
}
The error is gone. I have gone through so many stack overflow examples. But everywhere, it's given, we shouldn't name the field that starts with "is".
Could anyone please explain?
Thanks.

Hibernate use the getXxx and setXxx for the all the variable . So the getter the isManager variable getter getManager() is not available for hibernate.
so it get error. To resolve the issue.
You can generate the your own getter and setter as getManager() & setManager()
You can use the Boolean object instead of primitives.
Note: getIsManager is not meaningful to access the primitive boolean type. So Eclipse generate the getter for all the primitive start with isXXX as a getter.

Thanks for the comments.
I found the solution. When we name the field as isManager with the STS generated setter and getter, default value of Boolean, false is saved in DB.
field shouldn't start with auxiliary verb. According to my example:
#Column(name = "is_manager")
private boolean manager;
public boolean isManager() {
return employeeId;
}
public void setManager(boolean manager) {
this.manager = manager;
}
The above setter and getter works properly. I haven't updated the #param value in DB interface method.
Thanks.

Related

rest api return duplicate values in postman and spring boot h2

I am new spring boot developer and i am trying to develope and rest api . when I do it ,I get and issues that my api return two duplicated response in postman .But i haven't code anythiong to get duplicated valuese in my code . the one of duplicate values is my model clase variable and athor one is table's attribute name .
below response in postman
model class
public class person {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY )
private Long id;
#Column(name = "name")
private String Name ;
#Column(name ="surname")
private String Surname;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return Name;
}
public void setName(String name) {
Name = name;
}
public String getSurname() {
return Surname;
}
public void setSurname(String surname) {
Surname = surname;
}
}
repository
#Repository
public interface personRepository extends JpaRepository<person,Long> {
}
controller
#RestController
#RequestMapping("/person")
public class personController {
#Autowired
private personRepository repository;
public personController(personRepository repository) {
this.repository = repository;
}
#GetMapping("/view/list/person")
private List<person> viewperson() {
return repository.findAll();
}
#PostMapping("/insert/person")
private person savePerson(#RequestBody person obj) {
return repository.save(obj);
}
#DeleteMapping("/delete/{id}")
private void delete(#PathVariable Long id) {
repository.deleteById(id);
}
}
application.properties
spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialec
t
The problem is that you're not following the proper conventions in your naming strategy.
Due to this, Jackson doesn't know that your getters (getSurname(), getName()) are referencing the fields Surname and Name. That's why it serializes both your fields and your getters separately to JSON.
To fix this, you can follow the Java naming conventions and use a lowercase letter for the first character of your fields.
For example:
#Column(name = "name")
private String name; // Change this
#Column(name ="surname")
private String surname; // Change this
This will change your JSON output to:
{
"id": 1,
"name": "bryan",
"surname": "Nicky"
}
If you want to keep your JSON with capital letters, you can use the #JsonProperty annotation:
#JsonProperty("Name") // Add this
#Column(name = "name")
private String name;
#JsonProperty("Surname") // Add this
#Column(name ="surname")
private String surname;
Unrelated to your question, but according to those naming conventions, your classes should start with a capital (eg. Person, PersonController, PersonRepository, ...).

With Android Room, do I need to add setter and getter for nested object?

In the code below, class Address is nested in Entity User. I wonder if all the attributes of Address are private, do we need getter and setter for each of the field in Address? Notice there is a List<String>, so I'm not sure if Room will work well with #TypeConverter in this case.
public class Address {
public String street;
public String state;
public List<String> city;
#ColumnInfo(name = "post_code")
public int postCode;
}
#Entity
public class User {
#PrimaryKey
public int id;
public String firstName;
#Embedded
public Address address;
}
You can easily add getter/setters with #Ignore annotation and the converter will ignore these methods.
#Ignore
public List<String> getCity() {
return city;
}
You can refer here
Create the entity

Hibernate with mysql overwrites pk

Well, I'm using Hibernate for the first time and, unexpectedly, it works. Except for one thing: an insert with a pk already inserted overwrite the record instaed of preventing it.
That's my simple code:
#Controller
public class SimpleController {
#Autowired
UserRepository userRepository;
#GetMapping("/mainPage")
public String viewMainPage(){
return "mainPage";
}
#GetMapping("/nuovo-utente")
public String viewInserisciUtente(Model model){
model.addAttribute("nuovoUtente", new Utente());
return "nuovo-utente";
}
#PostMapping("/nuovo-utente")
public String memorizzaUtente(#ModelAttribute Utente utente){
userRepository.save(utente);
return "output";
}
}
#Entity
public class Utente {
#Id
private int id;
private String citta=null;
private String genere=null;
private String data_nascita=null;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCitta() {
return citta;
}
public void setCitta(String citta) {
this.citta = citta;
}
public String getGenere() {
return genere;
}
public void setGenere(String genere) {
this.genere = genere;
}
public String getData_nascita() {
return data_nascita;
}
public void setData_nascita(String data_nascita) {
this.data_nascita = data_nascita;
}
}
Any help will be appreciated.
EDIT: I've added the entity class to help you understanding my problem. Hoping that this will help.
Thanks you all
If you look at CrudRepository documentation, then we don't have update method, but we only have save method, which is used to add or update existing records.
In your case, you might have updated an entity (except its Id field) and tried saving the entity. So, CrudRepository will update the existing value for given Id, since it is already present.
Try adding ID generation strategy to id field.
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;

Extending/modifying generated entities in Hibernate

I am creating a REST api service for a mysql database. I've generated classes using IntelliJ's persistence tool. It does a pretty good job.
There are some quirks to the schema that I am working with. The users want the endpoints to be accessible by another property other than the "id" primary key column.
Ex: /object/<name property>' versus/object/`.
Here is the catch though. The schema can change. The name property is not going anywhere though so I can safely assume that will always be on the object.
I've learned that you can use Superclasses to force these generated entites to have custom properties without affecting the database schema. I dont want to make a model change in the generated entity and have that update the database table layout as it is not my database.
I have a class called Animal.
#Entity
#Table(name = "animals", schema = "xyz123", catalog = "")
public class AnimalEntity extends AnimalSuperclass {
private Integer id;
private String name;
private String description;
#Id
#Column(name = "id", nullable = false)
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
#Basic
#Column(name = "name", nullable = true, length = 80)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Basic
#Column(name = "description", nullable = true, length = 255)
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
RoleEntity that = (RoleEntity) o;
return Objects.equals(id, that.id) &&
Objects.equals(name, that.name) &&
Objects.equals(description, that.description);
}
#Override
public int hashCode() {
return Objects.hash(id, name, description);
}
}
I have to manually add extends AnimalSuperclass. Which is fine for now. Eventually I am going to try to generate these using .xmls on runtime.
Then I have this superclass..
#MappedSuperclass
public class AnimalSuperclass implements Serializable {
private String testMessage;
private String name;
private Integer id;
#Transient
public String getTestMessage() {
return this.testMessage;
}
public void setTestMessage(String id) {
this.testMessage = testMessage;
}
}
What I want to do is force the #Id annotation to be on the name property from within the superclass. Something like this..
#MappedSuperclass
public class AnimalSuperclass implements Serializable {
private String testMessage;
private String name;
private Integer id;
#Transient
public String getTestMessage() {
return this.testMessage;
}
public void setTestMessage(String id) {
this.testMessage = testMessage;
}
#Basic
#Id
#Column(name = "name", nullable = false, length = 15)
private String getName() {
return name;
}
private void setName(String name) {
this.name = name;
}
#NaturalId
#Column(name = "id", nullable = false)
private Integer getId() {
return id;
}
private void setId(Integer id) {
this.id = id;
}
}
How do I go about doing that? Currently this throws an error when I hit the endpoint: {"cause":null,"message":"Id must be assignable to Serializable!: null"}
Java is not my first language so I am not an expert by any means. But from what I've read, its not possible to override subclass properties from the superclass. Is there a better way to approach this, maybe by using RepositoryRestConfiguration? I am using PagingAndSortingRepository to serve these entities. I cannot extend the entities and use my superclass as a child as that creates a dType property in the schema and I cannot alter the table layout.
There is no hard link between the request and your entity. In your repository you can write methods that can query the data that is brought it from the request.
For example if they are requesting a name you can do something like
Page<AnimalEntity> findByName(String name, Pageable pageable);
in your Repository. Spring will take care of the rest and then you can call this in your controller.
#Service
public class AnimalService {
#Autowired
private AnimalEntityRepository animalRepo;
public Page<AnimalEntity> findAnimal(String name) {
Page<AnimalEntity> animals = animalRepo.findByName(name, new PageRequest(1,20));
return animals;
}
}
One thing to mention is that depending on how you configured Hibernate when sending an entity back to the client and the entity is seralized you might get an failed to lazy initialize error. If that is the case your entities will have to be converted to a POJO (plain old java object) and that sent back.

Setting up query for specific field of Hibernate entity

I use spring and hibernate. The following situation occured and I don't know if it's really possible to implement. Will appreciate any help.
For example, there is a hibernate entity
#Entity
public class TestEntity {
private String field1;
private String field2;
private String specField;
#Column(name = "field1")
public String getField1() {
return field1;
}
#Column(name = "field2")
public String getField2() {
return field2;
}
public String getSpecField() {
return (field1 != null ? field1 : field2);
}
}
And I need the value for specField to be generated by SQL query and not by java code.
Something like this
#Entity
public class TestEntity {
private String field1;
private String field2;
private String specField;
#Column(name = "field1")
public String getField1() {
return field1;
}
#Column(name = "field2")
public String getField2() {
return field2;
}
#Query(value= "COALESCE(field1, field2)")
public String getSpecField() {
return specField;
}
}
I was told that there should be ability to do so. But didn't find anything that approves this.
it's not actually important what exactly query does. I need that specField will be taken by some query and not by java code. Is it possible?
Thanks for help.
UPDATE Thanks to #premkumar, for advicing to use #Formula
So now I have
#Entity
public class TestEntity {
private String field1;
private String field2;
private String specField;
#Column(name = "field1")
public String getField1() {
return field1;
}
#Column(name = "field2")
public String getField2() {
return field2;
}
#Formula("COALESCE(field2, field2)")
public String getSpecField() {
return (field1 != null ? field1 : field2);
}
}
But app fails to start on bean initialization with
NullPointerException at org.springframework.orm.hibernate3.LocalSessionFactoryBean.newSessionFactory
I tried also the following:
Put formula above "private String specField" - app started, but hibernate failed on could not find spec_field in database
Put #Formula and #Transient on getter - app started, no errors, but specField is always null. It looks like hibernate totally ignored it
If you are using hibernate, try the following:
#Formula("COALESCE(field1, field2)")
public String getSpecField() {
return specField;
}
Note:- As far as I know there is no alternative in JPA. Beware this will mixup hibernate and jpa annotations.
Why not:
public String getSpecField() {
return MoreObjects.firstNonNull(field1,field2);
}
You can leverage the JPA Callback methods:
Create a EntityListener:
public class MyEntityListener {
#PrePersist
#PreUpdate
public void prePersist(Object object) {
if(object instanceOf TestEntity ) {
object.specField = object.field1 != null ? object.field1 : object.field2
}
}
}
Annotate your class with #EntityListener:
#Entity
#EntityListeners(MyEntityListener .class)
public class TestEntity {
private String field1;
private String field2;
private String specField;
//default cons - getters and setters
}

Categories

Resources