I am trying to figure it out how to do this proper way. Let's say I have entity Employee like that:
#Entity
public class EmployeeEntity{
#Id
private Long id;
private String username;
private String password;
private List<AddressEntity> addresses;
private DepartmentEntity department;
}
Now let's say some AddressEntity and DepartmentEntity are already created so I just want to point it. Controller would look like this:
#RestController
public class EmployeeController{
#Autowired
private EmployeeService;
#PostMapping
public EmployeeDto createEmployee(#RequestBody EmployeeDto employee){
return employeeService.createEmployee(employee);
}
}
And DTO:
public class EmployeeDto{
private Long id;
private String username;
private String password;
private List<AddressDto> addresses;
// private List<Long> addressesIds;
private DepartmentDto department;
// private Long departmentId;
}
So what bothers me is how properly transfer data from request to service layer and to response.
should DTO be object 1:1 same like entity?
or with additional values, like ids of others related objects?
or DTO is just concept and as well I can use custom request/response for every occasion? This would be handy but is it the way it should be done? There would be plenty of one-case-use classes.
Crating new entity is first problem but how about updating? If I would like to update just Employee username, I shouldn`t pass all rest of the objects so ids maybe? And it should be custom UpdateEmployeeRequest with only updatable fields or DTO with all data like password?
Sorry if I messed up a little. Too much new knowledge and I feel like I go round and round like a child in the fog...
should DTO be object 1:1 same like entity? or with additional values, like ids of others related objects?
Not necessary. DTOs are mostly to pass data to view layer. You can wrap data from multiple entities and send in one DTO to view.
or DTO is just concept and as well I can use custom request/response for every occasion? This would be handy but is it the way it should be done? There would be plenty of one-case-use classes.
Yes. It is like custom request/response for every occasion (data transfer to view and from view).
Crating new entity is first problem but how about updating? If I would like to update just Employee username, I shouldn`t pass all rest of the objects so ids maybe? And it should be custom UpdateEmployeeRequest with only updatable fields or DTO with all data like password?
Pass the required minimum fields and use same DTO on Create/Update (Id with field to update on update and other fields on Create).
Example dto for create:
username : "some user",
password : "some password",
... other fields
Example JSON for update username:
id: 1,
username : "some user",
Related
I´m having a relative complex, hierarchical data model in my Spring application and I need to query it as well as update all entities by letting the client pass the changed entity to the server.
Using GraphQL for querying solves exactly my needs, but to enable update and create capabilities however, I´d need to duplicate all my entities as *.graphqls files won´t support using "type" elements as "input" elements.
For the newly created Input element, I´d need to define a Java Object matching it too.
For queries I´d end up with
Portfolio.java
#Entity
public class Portfolio {
#Id
private int id;
private String name;
....
}
and portfolio.graphqls
type Portfolio {
id: ID!
name: String
....
}
But for updates I´d end up with
Portfolio.java
#Entity
public class Portfolio {
#Id
private int id;
private String name;
....
}
PortfolioInput.java
#Entity
public class PortfolioInput {
#Id
private int id;
private String name;
....
}
portfolio.graphqls
type Portfolio {
id: ID!
name: String
....
}
input PortfolioInput {
id: ID!
name: String
....
}
type PortfolioMutation{
updatePortfolio(input: PortfolioInput):Portfolio
}
My problem with that is, I need to keep 4 entities in sync with their fields now, just because GraphQL does not allow using the type as input and I´d need to do that for many other entities as well.
Is there any other solution, or do people go the extra mile and accept the additional complexity, or do you simply switch to REST for POST/PUT operations and only use GraphQL for GET?
This question already has answers here:
Infinite Recursion with Jackson JSON and Hibernate JPA issue
(29 answers)
Closed 3 years ago.
I am trying to create Many to one mapping between two entities in spring. However when I try to fetch the values using a restController I get
Java.lang.IllegalStateException: Cannot call sendError() after the
response has been committed
error and an infinite JSON response. Adding JSON ignore solves this issue but then I don't get that column in my response at all. I don't know how to fix this. Please help. Thanks in advance.
Here are my entities:
#Entity
#Table(name="tbl1")
public class Data {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "customer")
private String customer;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name="data_id", insertable = true, nullable = false, updatable = false)
private DataList dataList1;
}
#Entity
#Table(name="tbl2")
public class DataList {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="data_id")
private Long dataId;
#Column(name="user_name")
private String userName;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER,mappedBy = "dataList1")
private List<Data> data1;
}
Repositories:
#Repository
#Transactional
public interface DataList extends JpaRepository<DataList,Long> {
}
#Repository
#Transactional
public interface Data extends JpaRepository<Data,Long> {
}
My error: Java.lang.IllegalStateException: Cannot call sendError() after the response has been committed
1) In general putting fetch = FetchType.EAGER on #OneToMany is not an advisable practice. Try to set it to LAZY instead.
2) Make sure that Data and DataList entities properly implement equals() and hashCode() methods.
It is happening as JSON serializer is trying to serialize both the entities recursively again and again.
So, while serializing Data, its member dataList1 is of type DataList which further contains List<Data>, this loop will be infinite.
Ideally, in such scenarios, the entities should be mapped to some other model meant for the serialization and response or one of the model needs to be #JsonIgnore to avoid this recursive loop going.
EAGER is a bad practice. Use LAZY, and when you need the related entities, use fetch join query.
Most likely the problem here in the bi-directional relation. You fetch DataList with Data list. And each Data in List ListData refers again.
More likely here to be a stack overflow when serializing json. So remember one rule: never give in controllers hibernate entities. Write a mapper for map this entities to Dto objects.
You may think that it is extremely boring to map some models to another. But here in another way. Hibernate entities should not be passed to front end. My application uses several types of objects: Entities (when fetch from DB), DTO (When map entities to DTO and give their in service components), Shared (when map DTO to Shared and share as a response to the controller between my microservices) and Model (when map from Response to Model and give to the template engine, for example freemarker). You may not need this hierarchy of models.
Create DTO's:
#AllArgsConstructor
#NoArgsConstructor
#Getter
public class DataListDto {
private Long dataId;
private String userName;
private List<DataDto> data1;
}
#AllArgsConstructor
#NoArgsConstructor
#Getter
public class DataDto {
private long id;
private String customer;
}
write your mapper:
public class DataListMapper {
public static DataListDto mapToDto(DataList dataList) {
return new DataListDto(dataList.getDataId(), dataList.getUserName(), dataList.getData1().stream().map(x -> DataListMapper.mapToDto(x).collect(Collectors.toList)))
}
public static DataDto mapToDto(Data data) {
return new DataDto(data.getId(), data.getCustomer());
}
}
your service:
public class DataListService {
#Autowired
private DataListRepository dataListRepository;
public List<DataListDto> getAllData() {
List<DataList> dataLists = this.dataListRepository.findAll();
return dataLists.stream().map(x->DataListMapper.mapToDto(x)).collect(Collectors.toList());
}
}
Mongodb is a no-schema document database, but in spring data, it's necessary to define entity class and repository class, like following:
Entity class:
#Document(collection = "users")
public class User implements UserDetails {
#Id private String userId;
#NotNull #Indexed(unique = true) private String username;
#NotNull private String password;
#NotNull private String name;
#NotNull private String email;
}
Repository class:
public interface UserRepository extends MongoRepository<User, String> {
User findByUsername(String username);
}
Is there anyway to use map not class in spring data mongodb so that the server can accept any dynamic JSON data then store it in BSON without any pre-class define?
First, a few insightful links about schemaless data:
what does “schemaless” even mean anyway?
“schemaless” doesn't mean “schemafree”
Second... one may wonder if Spring, or Java, is the right solution for your problem - why not a more dynamic tool, such a Ruby, Python or the Mongoshell?
That being said, let's focus on the technical issue.
If your goal is only to store random data, you could basically just define your own controller and use the MongoDB Java Driver directly.
If you really insist on having no predefined schema for your domain object class, use this:
#Document(collection = "users")
public class User implements UserDetails {
#Id
private String id;
private Map<String, Object> schemalessData;
// getters/setters omitted
}
Basically it gives you a container in which you can put whatever you want, but watch out for serialization/deserialization issues (this may become tricky if you had ObjectIds and DBRefs in your nested document). Also, updating data may become nasty if your data hierarchy becomes too complex.
Still, at some point, you'll realize your data indeed has a schema that can be pinpointed and put into well-defined POJOs.
Update
A late update since people still happen to read this post in 2020: the Jackson annotations JsonAnyGetter and JsonAnySetter let you hide the root of the schemaless-data container so your unknown fields can be sent as top-level fields in your payload. They will still be stored nested in your MongoDB document, but will appear as top-level fields when the ressource is requested through Spring.
#Document(collection = "users")
public class User implements UserDetails {
#Id
private String id;
// add all other expected fields (getters/setters omitted)
private String foo;
private String bar;
// a container for all unexpected fields
private Map<String, Object> schemalessData;
#JsonAnySetter
public void add(String key, Object value) {
if (null == schemalessData) {
schemalessData = new HashMap<>();
}
schemalessData.put(key, value);
}
#JsonAnyGetter
public Map<String, Object> get() {
return schemalessData;
}
// getters/setters omitted
}
Here is a simplified POJO i have:
#Entity
#Table( name = "Patient" )
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn
(
name="Discriminator",
discriminatorType=DiscriminatorType.STRING
)
#DiscriminatorValue(value="P")
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Patient implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name = "ID", unique = true, nullable = false)
protected Integer ID;
#ManyToOne(targetEntity = TelephoneType.class, fetch=FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name="IDPhoneType")
protected TelephoneType phoneType;
#JsonProperty(required=false, value="phoneType")
public TelephoneType getPhoneType() {
return phoneType;
}
public void setPhoneType(TelephoneType phoneType) {
this.phoneType = phoneType;
}
}
Now here is my class TelephoneType:
#Entity
#Table( name = "TelephoneType" )
#Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
#JsonAutoDetect(getterVisibility=Visibility.NONE, isGetterVisibility=Visibility.NONE, fieldVisibility=Visibility.NONE)
public class TelephoneType implements Serializable{
private static final long serialVersionUID = -3125320613557609205L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name = "ID", unique = true, nullable = false)
private Integer ID;
#Column(name = "Name")
private String name;
#Column(name = "Description")
private String description;
public TelephoneType() {
}
#JsonProperty(value="id")
public int getID() {
return ID;
}
public void setID(int iD) {
ID = iD;
}
#JsonProperty(value="name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#JsonProperty(value="description")
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
The reason i use the #JsonAutoDetect annotation in TelephoneType is first to customize the json property names (i needed to deactivate the default jsonautodetect) and also because if I don't, I get an error when fetching the Queue
No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: my.package.Patient["phoneType"]->my.package.TelephoneType_$$_jvste17_13["handler"])
So without the #JsonAutoDetect annotation i get the error and with the annotation no Lazy Loading occurs and the TelephoneType is always loaded in the json response.
I use Criteria to make the query:
return this.entityManager.find(Patient.class, primaryKey);
I also added, as I read in different posts on so, the following in the web.xml of my application (Jersey API):
<filter>
<filter-name>OpenEntityManagerInViewFilter</filter-name>
<filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>OpenEntityManagerInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Now somehow I surely missed something in my configuration but can't figure out what and we have many #ManyToOne relationships in the db that are slowing down the api considerably (some heavier objects than the one I showed in the example) so I would really appreciate to find a way to activate this lazy loading thing...
If you are using JSON then I presume that you are supplying the results through a REST endpoint. What is happening then is you are passing the Patient entity back to the REST service. When the REST service, Jersey in this case, serializes the Patient entity it touches all of the properties, and even walks through them, so as to build as complete a tree as possible. In order to do this, every time Jersey hits a property that's not yet initialized, Hibernate makes another call back to the database. This is only possible if the EntityManager is not yet closed.
This is why you have to have the OpenEntityManagerInViewFilter installed. Without it, the EntityManager is closed when you exit the service layer and you get a LazyInitializationException. The OpenEntityManagerInViewFilter opens the EntityManager at the view level and keeps it open until the HTTP request is complete. So, while it seems like a fix, it's not really because, as you see, when you lose control over who is accessing the properties of your entities, in this case Jersey, then you end up loading things you didn't want to load.
It's better to remove the OpenEntityManagerInViewFilter and figure out what exactly you want Jersey to serialize. Once you have that figured out, there are at least two ways to go about handling it. IHMO, the "best practice" is to have DTO, or Data Transfer Objects. These are POJOs that are not entities but have pretty much the same fields. In the case, the PatientDTO would have everything except the phoneType property (or maybe just the Id). You would pass it a Patient in the constructor and it would copy the fields you want Jersey to serialize. Your service layer would then be responsible for returning DTO's instead of Entities, at least for the REST endpoints. Your clients would get JSON graphs that represent these DTOs, giving you better control over what goes into the JSON because you write the DTOs separate from the Entities.
Another option is to use JSON annotations to prevent Jersey from attempting to serialize properties you don't want serialized, such as phoneType, but that ultimately becomes problematic. There will be conflicting requirements and you never get it sorted out well.
While making DTO's at first seems like a horrible pain, it's not as bad as it seems and it even helps when you want to serialize values that are more client friendly. So, my recommendation is to lose the OpenEntityManagerInViewFilter and construct a proper service layer that returns DTOs, or View Objects as they are sometimes called.
References: What is Data Transfer Object?
REST API - DTOs or not?
Gson: How to exclude specific fields from Serialization without annotations
To understand what is happening here you have to understand how lazy loading works in Hibernate.
When a list is declared as "lazy loaded" the Hibernate framework implements a "lazy loaded" JavassistLazyInitializer object with Javassist.
Hence, the phoneType on your patient object is not an implementation of your TelephoneType class. It is a proxy towards it.
When getPhoneType() on this object is called however, the proxy on patient is replaced by the real object.
Unfortunately, #JsonAutoDetect uses reflection on the proxy object without ever calling getPhoneType() and tries to actually serialise the JavassistLazyInitializer object which of course is impossible.
I think the most elegant solution for this is to implement a query that fetches the patients with their telephoneType.
So instead of:
return this.entityManager.find(Patient.class, primaryKey);
Implement something like:
EntityManager em = getEntityManager();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Patient> query = cb.createQuery(Patient.class);
Root<Patient> c = query.from(Patient.class);
query.select(c).distinct(true);
c.fetch("phoneType");
TypedQuery<Patient> typedQuery = em.createQuery(query);
List<Patient> allPatients = typedQuery.getResultList();
Adapting the query to your needs as required.
Have a look at jackson-datatype-hibernate
It makes the json serialization with jackson 'aware' of the hibernate proxy
I've created an entity with a pojo (ProductVariations) using the label #Serialize to be persisted in GAE through objectify:
#Entity
public class Product extends DatastoreObject{
//Reference without the colors and size information
#Index private String ref;
private double price;
private String details;
private String description;
#Serialize private ProductVariations pVariations;
private List<String> tags = new ArrayList<String>();
//Getters & Setters
}
The problem is that I don't see how to access my pojo with requestfactory because ProductVariations is not a domain type.
In any other case I would use an embeded object but in this particular case I have a nested collection inside ProductVariations witch is a collection in itself (ProductVariations extends ArrayList).
Any suggestions in how to achieve this?
Thank you.
Not sure I understand your question, but you need to implement Serializable in Product if you want to send it over RPC.
Beyond that, are you having problems storing ProductVariations? It's an interesting concept. If it isn't working:
Can you keep ProductVariations in its own #Entity?
Then keep a Key in Product class (or a Long that can you can create a Key from).
For convenience you can also leave ProductVariations in Product but mark it with #Transient and then populate it from the Key/Long in the factory that does your ofy.get().