No getters and setters are called in JPA - java

I am using JPA 2.1 and while persisting and retrieving the entities from database I could see no constructor of the entity is called and not getters and setters. How does the serialization and deserialization take place then from DB object to JAVA object, if getters, setters and constructor are not called
Teacher
#Entity
#Table(name="Teacher")
public class Teacher {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
int id;
#Column
String name;
public Teacher(String name) {
super();
this.name = name;
}
public Teacher()
{
System.out.println("const");
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#Column
public String getName() {
System.out.println("get Name");
return name;
}
public void setName(String name) {
System.out.println("set Name");
this.name = name;
}
Main
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("persistence");
EntityManager em1 = entityManagerFactory.createEntityManager();
em1.getTransaction().begin();
Teacher t = new Teacher("wick");
em1.persist(t);
Teacher t1= em1.find(Teacher.class, 21);
em1.getTransaction().commit();
em1.close();

Hibernate and similar libraries and frameworks often don't use (and require) getters and setters for accessing the properties of an objects. Java has several methods for inspecting and manipulating objects directly. This is called reflection. The entry point for this are often the method of the class Class from the Java Standard API. Each class in Java has an associated instance of these class which provides several methods for getting information about the class. For example the method getDeclaredFields returns and array with data about all fields in the class. If you look into the documentation of the Field class you see that there are several set methods which take an object (the instance of the object on which the value is set) and a value as parameters. This methods can also be used to bypass the access modifiers like private etc.
Best
Jens

Related

Java Spring Neo4jRepository unable to read a property of abstract type

I have just started experimenting with Neo4J/Java and expect this is an easy one I'm missing, and probably phrasing my queries wrong.
I have some model classes as follows:
#Node
public class Garment {
#Id
#GeneratedValue
private Long id;
private String name;
#Relationship(type = "DESIGNED_BY")
private Entity designer;
// Other properties getters/setters removed for readibility
public Entity getDesigner() {
return designer;
}
public void setDesigner(Entity designer) {
this.designer = designer;
}
}
public abstract class Entity {
#Id
#GeneratedValue
Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
#Node
public class Person extends Entity {
}
#Node
public class Company extends Entity {
}
And a corresponding repository
public interface Garment extends Neo4jRepository<Garment, Long> {
Garment findByName(String name);
}
I have no problem inserting, using repository.save(), this correctly adds everything; nodes, relationships. Fine. It gives Designers of type Person labels of Person, and Designers of type Company the label Company.
However, when I do a find, e.g. findByName(), findAll(). it is not matching the designer and just saying designer is null, according to the cipher being executed/logged it looks like it's trying to build a relationship there with nodes with an Entity label, which there are none.
How can I get my repository to return Garments with designers of Person and Companys. I expect this is going to be as simple as an annotation, in order to fix.
(Note I've tried adding a #Node on the entity type with Person and Company as labels, however it just results in every node being added as both a Person and a Company).

Why is Jackson mapping these values twice, in differing case?

I'm mapping a Java object to JSON using Jackson, the object is a pretty simple pojo class that looks like this:
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonProperty;
#JsonAutoDetect
public class Area {
#JsonProperty("Id")
public int Id;
#JsonProperty("Name")
public String Name;
public Area() {
Name = "";
}
public int getId() {
return Id;
}
public void setId(int id) {
Id = id;
}
public String getName() {
return Name;
}
public void setName(String Name) {
this.Name = Name;
}
}
The mapping code then looks like this:
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibilityChecker(mapper.getSerializationConfig().getDefaultVisibilityChecker()
.withFieldVisibility(JsonAutoDetect.Visibility.ANY)
.withGetterVisibility(JsonAutoDetect.Visibility.NONE)
.withSetterVisibility(JsonAutoDetect.Visibility.NONE)
.withCreatorVisibility(JsonAutoDetect.Visibility.NONE));
areaJSON = mapper.writeValueAsString(area);
But the value of areaJSON at this point is then as follows:
{"id":0,"name":"","Name":"","Id":0}
Note the multiple values with differing case.
What am I doing wrong?
Jackson thinks that the fields Id and Name are different properties from the ones returned by the getters because the case is different. Using standard JavaBeans naming conventions, Jackson infers the fields corresponding to the getters are named id and name, not Id and Name.
tl;dr case matters.
There are two simple ways to fix this problem:
Remove the #JsonAutoDetect annotation from this class entirely. I'm pretty sure that the default annotation values are taking precedence over the ObjectMapper's configuration. Alternately:
Don't muck with the ObjectMapper at all. Change the #JsonAutoDetect on the class to
#JsonAutoDetect(
fieldVisibility = Visibility.ANY,
getterVisibility = Visibility.NONE,
setterVisibility = Visibility.NONE,
creatorVisibility = Visibility.NONE
)
You need to annotate getId method with #JsonProperty("Id"), otherwise getId will also be added with lowercase id.
I know that it's an old post, but there is a simpler solution:
use only annotation on fields:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Area {
public int id;
public String name;
public Area() {
name = "";
}
public int getId() {
return id;
}
public void setId(int id) {
id = id;
}
public String getName() {
return name;
}
public void setName(String Name) {
this.name = Name;
}
}
You can choose how to serialize the object: using the field or using the properties. If you use the fields, the getter and setter are ignored.
The problem in the code is created by the first letter uppercase : accessing the field, the json property is Id; accessing the getter , getId became id (first letter after get is coded in lower case).
The solution for me was to move annotations to either setters or getters (either one is fine)

Save entity after unique constraint exception

i have an entity with a unique name.
In my example i save two persons with the same name. At the second time comes an "EntityExists" (Unique) Exception, that was the expected behavior.
After it i changed name and set the "ID" to null.
Than i try to persist it again but i get "org.apache.openjpa.persistence.EntityExistsException: Attempt to persist detached object "com.Person#1117a20". If this is a new instance, make sure any version and/or auto-generated primary key fields are null/default when persisting.
without the version it works but i find no solution to "reset" the version number.
Can someone help me?
Update: My new problem is, that i have a base entity an two pcVersionInit (look at my answer at bottom) i can't override it, i tried it in base and normal entity what is the best practise now instead of "override" the value in pcVersionInit ? Copy Constructor?"
public class Starter{
private static EntityManager em;
public static void main(String[] args) {
em = Persistence.createEntityManagerFactory("openjpa")
.createEntityManager();
Person p1 = new Person("TEST");
savePerson(p1);
Person p2 = null;
try{
p2 = new Person("TEST");
savePerson(p2);
}catch(Exception e){
p2.setId(null);
p2.setName(p2.getName()+"2");
em.persist(p2);
}
}
private static void savePerson(Person person){
em.getTransaction().begin();
em.persist(person);
em.getTransaction().commit();
}
}
Person.class:
#Entity
public class Person implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE,generator="seqGenerator")
#SequenceGenerator(name="seqGenerator",sequenceName="personSeq")
private Long id;
#Version
private Long version;
#Column(nullable=true, unique=true)
private String name;
public Person(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getVersion() {
return version;
}
public void setVersion(Long version) {
this.version = version;
}
}
First off, stay away from messing around with pcVersionInit. I'd suggest to create a copy constructor in your Person Entity and in the event of rollback create a new one using the copy constructor.
Okay the problem is that OpenJPA adds a field named pcVersionInit (with #version) and set it "true" after try to persist. If i use reflection to set it to false, it works. Other way is a copy constructor.

JSR-303 Validation on sub-class

I have the following class structure
public Abstract class Person {
private String fullName;
private Address address;
private Phone ;
}
class Staff extends Person{
private String staffId;
}
I want to apply validation using JSR-303 on class Staff whereby Staff address and phone cannot have the value of null. However, I have some other classes that are class of Person where I don't wish to have the validation to be applied.
One way to do this that I could think of is by refactor Person and push the fields 'address' and 'phone' to Staff, but this means refactoring a lot of other classes (and not to mention redundancy this shall cause), and hence something I want to avoid.
Update.
I have changed Staff class, as follows
public class Staff extends Person {
#NotNull
private String staffEmploymentId;
public String getStaffEmploymentId() {
return staffEmploymentId;
}
public void setStaffEmploymentId(String id) {
this.staffEmploymentId = id;
}
#Override
#NotNull
public void setPhones(List<Phone> phones) {
super.phones = phones;
}
#Override
#NotNull
public void setAddress(Address a) {
super.address = a;
}
#Override
#NotNull
public Address getAddress(){
return super.address;
}
}
However, I've got the following error.
javax.validation.ValidationException: Property setAddress does not follow javabean conventions.
I am using Apache BVal, as opposed to Hibernate Validator.
Annotate getters instead of fields using annotations from JSR-330.
You can override getters in Stuff and annotate them.

How to convert field of the list item via custom Struts type converter?

I need to implement custom conversion for ID field in Company and Employee classes. I have already implemented custom converter extended from StrutsTypeConverter and it is successfully used to convert Company.ID field, but it does not work for Employee.ID.
Seems like the main problem is in conversion properties file. How should I specify converter class for employee ID field in conversion properties file?
MyAction-conversion.properties:
company.id = com.struts2.convertors.MyCustomConverter
company.??????.id = com.struts2.convertors.MyCustomConverter
MyAction:
public class MyAction extends ActionSupport {
private Company company;
public Company getCompany () {
return company;
}
public void setCompany (Company company) {
this.company= company;
}
#Override
public String execute() {
return SUCCESS;
}
}
Company:
public class Company {
private ID id;
private List<Employee> employees;
// getters and setters
}
Employee
public class Employee{
private ID id;
private String name;
// getters and setters
}
TypeConversion Annotation:
This annotation is used for class and application wide conversion rules.
The TypeConversion annotation can be applied at property and method level.
#TypeConversion(converter = “com.test.struts2.MyConverter”)
public void setAmount(String amount)
{
this.amount = amount;
}
This annotation specifies the location of one of my converters. literally, by using this annotation, I register my class com.test.struts2.MyConverter as a converter, and gets executed every time when setAmount(String amount) method is invoked.
Try the following by adding a converter for the ID type to the xwork-conversion.properties file
com.struts2.ID = com.struts2.convertors.MyCustomConverter

Categories