Spring data rest is not saving all field of my entity - java

I have the following #Entity:
#Entity
public class Person {
#Id #GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private Date birthDate;
private String status;
private String city;
...
// many more attributes
}
I'm using the spring data rest as follow:
#RepositoryRestResource(collectionResourceRel = "person", path = "person")
public interface PersonRepositorio extends PagingAndSortingRepository<Person, Long>{
}
When I send the a post to the /api/person/ with a JSON containing all attributes of Person, only status is not set. Can someone help me?

Well, I think you're missing the get and set for status. Did you check it?

Related

How can I include references in spring rest response?

I have defined two JPARepository with spring: "Person" and "Address". I also specified a relation between Person and Address.
I can fetch all persons with:
http://localhost:8080/person/
and all address with: http://localhost:8080/address/
Also I can get the address of a single person with http://localhost:8080/person/1/address
Now I'd like to get the address to every person as a nested address when I get request: http://localhost:8080/person/
How can I include the relations in the response?
My classes as requested by #nicolasl
#Entity
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String firstName;
private String lastName;
#OneToOne
#JoinColumn(name = "address_id")
private Address address;
//Getter/Setter here
}
#Entity
public class Address {
#Id
#GeneratedValue
private long id;
#Column(nullable = false)
private String location;
#OneToOne(mappedBy = "address")
private Person person;
//Getter/Setter here
}
Well, I guess you just can't, not using the automated rest engine provided by spring. The rest API will return links to retrieve the related entities in the "_links" section of your JSON response (you can read more about that in http://stateless.co/hal_specification.html). You'd have to call those links to retrieve your Addresses.
Another way to solve this, is implementing your own repository and assembling your own JSON response.

Spring Data Rest post fails to save nested object

I have these Objects:
#Data
#Entity
#Table
#EqualsAndHashCode(callSuper = true)
public class User extends AbstractEntity implements Serializable {
private static final long serialVersionUID = -55089179131569489L;
private String username;
private String email;
private boolean admin;
private String name;
private String surname;
#OneToMany(mappedBy = "owner")
private List<Ad> ads;
}
and
#Entity
#Table
#Data
#EqualsAndHashCode(callSuper = true)
public class Ad extends AbstractEntity implements Serializable {
private static final long serialVersionUID = -4590938091334150254L;
private String name;
private String description;
private double price;
#Enumerated(EnumType.STRING)
private Category category;
#ManyToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn(name = "OWNER_ID")
private User owner;
}
When I try to execute a POST with an object of type Ad.class with inside an existing object of type User.class (already in the Database) the service saves only the Ad object and the join column "OWNER_ID" remains empty.
I think that the mapping is correct. Could you help me to figure out the problem?
This is my Repository:
#Repository
#Transactional(readOnly = true)
public interface AdRepository extends PagingAndSortingRepository<Ad, String>
{}
and this is my RestRepository
#RepositoryRestResource(collectionResourceRel = "ad", path = "ad")
public interface AdRestRepository extends PagingAndSortingRepository<Ad, String> {}
If I step back a little and generalize your problem,
You are trying to POST a sub resource and expect both actions of
making a new resource (Ad)
making association with the owner (User)
to be happened with a single call.
But unfortunately spring-data-rest does not support such a behavior. You need 2 calls to do this.
One to make the resource (Ad) => POST to /ads with actual payload
Second to make the association => POST to users/{ownerId} with the hateoas link of the resource created by the first call.
Take a look at this section of official documentation.

Deserializing JSON from Hibernate

I'm having a problem de deserializing a class in Spring Boot. When my controller tries to deserialize it, it crashes. Here is the class:
#Entity
#Table(name="trash_cans")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property="id")
public class TrashCan {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="TRASH_CAN_ID")
long id;
#Column(name="TRASH_CAN_NAME")
String name;
#ManyToOne
#JoinColumn(name="PLACE_ID")
private Place place;
#OneToMany(mappedBy="trashCan", targetEntity=TrashMeter.class, fetch=FetchType.LAZY)
private List<TrashCan> trashMeterList;
#OneToMany(mappedBy="trashCan", targetEntity=TrashSensor.class, fetch=FetchType.LAZY)
private List<TrashSensor> trashSensorList;
public TrashCan() {
}
public TrashCan(long id, String name) {
super();
this.id = id;
this.name = name;
}
[getters and setters]
}
That depends on this one:
#Entity
#Table(name="trash_sensor")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property="id")
public class TrashSensor {
#Id
#Column(name="id")
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
#Column(name="name")
private String name;
#Column(name="description")
private String description;
#ManyToOne
#JoinColumn(name="TRASH_CAN_ID")
private TrashCan trashCan;
#OneToMany(mappedBy = "trashSensor", targetEntity = Measurement.class, fetch = FetchType.LAZY)
private List<Measurement> measurementList;
public TrashSensor() {
super();
}
And Trash Sensor Depends on this Class:
#Entity
#Table(name="measurement")
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property="id")
public class Measurement {
#Id
#Column(name="id")
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
#Column(name="value")
private float value;
#Column(name="last_measure")
private LocalDateTime dateTime;
#ManyToOne
#JoinColumn(name="trash_sensor_id")
private TrashCan trashSensor;
public Measurement() {
}
}
My Controler:
#RequestMapping(value="/trashCan", method=RequestMethod.GET)
public ResponseEntity<Iterable<TrashCan>> getPlaces(){
Iterable<TrashCan> trashCanIterable = trashCansRepository.findAll();
return new ResponseEntity<>(trashCanIterable, HttpStatus.OK);
}
When I call the webservice, I get this error:
Failed to write HTTP message: org.springframework.http.converter.HttpMessageNotWritableException: Could not write content: could not deserialize (through reference chain: java.util.ArrayList[0]-br.com.simplepass.cleanerway.domain.TrashCan["trashSensorList"]-org.hibernate.collection.internal.PersistentBag[0]-br.com.simplepass.cleanerway.domain.TrashSensor["measurementList"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: could not deserialize (through reference chain: java.util.ArrayList[0]-br.com.simplepass.cleanerway.domain.TrashCan["trashSensorList"]-org.hibernate.collection.internal.PersistentBag[0]-br.com.simplepass.cleanerway.domain.TrashSensor["measurementList"])
I can't interpret this error =/. Any help with this problem greatly appreciated.
You are getting this error since your json is entering a loop, to avoid this, use #JsonIgnore annotation:
#OneToMany(mappedBy = "trashSensor", targetEntity = Measurement.class, fetch = FetchType.LAZY)
#JsonIgnore
private List<Measurement> measurementList;
It happens when you use relations between entities. Imagine that your TrashCan has link to Trash in it. And your trash has link to it's wrapper - trashcan. So what you try to serialize TrashCan entity you also serializing Trash. And then when you are serializing trash trashcan is serialized again inside it. And so on. It's a loop. You can use #JsonIgnore on every entity that may cause loop.
#JsonIgnore
#ManyToOne
#JoinColumn(name="PLACE_ID")
private Place place;
#JsonIgnore
#OneToMany(mappedBy="trashCan", targetEntity=TrashMeter.class, fetch=FetchType.LAZY)
private List<TrashCan> trashMeterList;
#JsonIgnore
#OneToMany(mappedBy="trashCan", targetEntity=TrashSensor.class, fetch=FetchType.LAZY)
private List<TrashSensor> trashSensorList;
But it's a bad way. It's strongly recommended to use DTO (Data transfer object) pattern for you serialization/deserialization. It also gives you more flexibility. You can read about it here
If you need trashMeterList and trashSensorList in response then follow this answer.
Due to hibernate lazy loading and no session while deserialisation, you are getting this exception.
To fix just change your controller:
#RequestMapping(value="/trashCan", method=RequestMethod.GET)
public ResponseEntity<Iterable<TrashCan>> getPlaces(){
Iterable<TrashCan> trashCanIterable = trashCansRepository.findAll();
List<TrashCan> responseList = new ArrayList<TrashCan>(trashCanIterable.size())
while(trashCanIterable.hasNext()){
TrashCan trashCan = trashCanIterable.next();
for(TrashMeter trashMeter : trashCan.trashMeterList){
}
for(TrashSensor trashSensor : trashCan.trashSensorList){
}
responseList.add(trashCan);
}
return new ResponseEntity<>(responseList, HttpStatus.OK);
}

PSQLException: ERROR: syntax error at or near

I have what I thought was a straight forward relation in JPA. Looks like this. CompanyGroup:
#Entity
#Table
public class CompanyGroup implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
private Long id;
#Column(name = "name")
private String name;
#JoinColumn(name = "companies")
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<Company> companies;
}
Company:
#Entity
#Table
public class Company implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "name")
private String name;
#JoinColumn(name = "users")
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<User> users;
#Id
#GeneratedValue
private Long id;
}
User:
#Entity
#Table
public class User {
#Column(name = "firstName")
private String firstName;
#Column(name = "lastName")
private String lastName;
#Column(name = "email")
private String email;
#Id
#GeneratedValue
private Long id;
}
I have omitted setters, getters, etc.
This is not working. I'm trying to save a CompanyGroup(Has 2 companies, each company has 2 users, all entities are unique) to a fully empty database.
I persist this using Spring-Data, accessed in a service like this:
#Service
public class ConcreteCompanyGroupService implements CompanyGroupService {
#Autowired
private CompanyGroupRepository repository;
#Transactional
#Override
public void save(CompanyGroup group) {
repository.save(Collections.singleton(group));
}
}
When I try to call this method I receive this:
org.postgresql.util.PSQLException: ERROR: syntax error at or near "User"
Position: 13
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2458)
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2158)
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:291)
Hopefully I have done something stupid that someone can find quickly. I don't know how to solve this.
EDIT:
The driver in my pom.xml:
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.4.1211</version>
</dependency>
Your entity maps across to a table name that is an SQL reserved keyword (User). Sadly for you, your chosen JPA provider does not automatically quote the table name identifier, and so you get exceptions when referring to the table.
Solution is either to quote the table name yourself in the #Table annotation, or change the table name to not be a reserved keyword. Alternatively use a JPA provider that auto-quotes such reserved keywords for you (e.g DataNucleus)
Solution 1: As Pascal mentioned, you have to escape the table name with backslash like:
#Entity
#Table(name="\"User\"")
public class User {
...
}
Solution 2: Rename your table's anme with another name (Users)
#Entity
#Table(name="Users")
public class User {
...
}
Solution 3: Add a suffix to the table's name:
#Entity
#Table(name="APP_User")
public class User {
...
}
Solution 4: Change the entity name, e.g. ApplicationUser
#Entity
public class ApplicationUser {
...
}
The reason
PostgreSQL as some reserved SQL Key Words. For example: ABORT, ALL, ARRAY, CACHE, CUBE, USER, ... Those tokens are in the SQL standard or specific to PostgreSQL
Use the #Table annotation or change your class name from User to something else as User is a reserved keyword in sql.

Ebean and Play! not filtering columns with .select()

I'm trying to fetch just a part of the model using Ebean in Play! Framework, but I'm having some problems and I didn't found any solutions.
I have these models:
User:
#Entity
#Table(name = "users")
#JsonInclude(JsonInclude.Include.NON_NULL)
public class User extends Model{
#Id
private int id;
#NotNull
#Column(name = "first_name", nullable = false)
private String firstName;
#Column(name = "last_name")
private String lastName;
#NotNull
#Column(nullable = false)
private String username;
#NotNull
#Column(nullable = false)
private String email;
private String gender;
private String locale;
private Date birthday;
private String bio;
#NotNull
#Column(nullable = false)
private boolean active;
private String avatar;
#Column(name = "created_at",nullable = false)
private Date createdAt;
#OneToMany
private List<UserToken> userTokens;
// Getters and Setters omitted for brevity
}
UserToken:
#Entity
#Table(name = "user_tokens")
public class UserToken extends Model {
#Id
private int id;
#Column(name = "user_id")
private int userId;
private String token;
#Column(name = "created_at")
#CreatedTimestamp
private Date createdAt;
#ManyToOne
private User user;
// Getters and Setters omitted for brevity
}
And then, I have a controller UserController:
public class UserController extends Controller{
public static Result list(){
User user = Ebean.find(User.class).select("firstName").where().idEq(1).findUnique();
return Results.ok(Json.toJson(user));
}
}
I expected that, when using the .select(), it would filter the fields and load a partial object, but it loads it entirely.
In the logs, there is more problems that I don't know why its happening.
It is making 3 queries. First is the one that I want. And then it makes one to fetch the whole Model, and another one to find the UserTokens. I don't know why it is doing these last two queries and I wanted just the first one to be executed.
Solution Edit
After already accepted the fact that I would have to build the Json as suggested by #biesior , I found (out of nowhere) the solution!
public static Result list() throws JsonProcessingException {
User user = Ebean.find(User.class).select("firstName").where().idEq(1).findUnique();
JsonContext jc = Ebean.createJsonContext();
return Results.ok(jc.toJsonString(user));
}
I render only the wanted fields selected in .select() after using JsonContext.
That's simple, when you using select("...") it always gets just id field (cannot be avoided - it's required for mapping) + desired fields, but if later you are trying to access the field that wasn't available in first select("...") - Ebean repeats the query and maps whole object.
In other words, you are accessing somewhere the field that wasn't available in first query, analyze your controller and/or templates, find all fields and add it to your select (even if i.e. they're commented with common HTML comment in the view!)
In the last version of Play Framework (2.6) the proper way to do this is:
public Result list() {
JsonContext json = ebeanServer.json();
List<MyClass> orders= ebeanServer.find(MyClass.class).select("id,property1,property2").findList();
return ok(json.toJson(orders));
}

Categories

Resources