Hibernate: Populate Some Fields From Web Service - spring

In our Spring MVC - Hibernate project we store uploaded files through a web service. However, we also keep information related to the file in a table in our database. A simplified version of the File class is as below:
#Entity
#Table(name="FILE")
public class File {
#Id
#Column(name="ID")
public int id;
#Column(name="NAME")
public String name;
#Transient
public byte[] data;
public int getId() {
}
public void setId(int id) {
}
public String getName() {
}
public void setName(String name) {
}
public byte[] getData() {
}
public void setData(byte[] data) {
}
}
We load the File entity from our database and fetch its data from a web service as below:
File file = dao.getFileById({file_id});
byte[] data = webService.getFileData({file_id});
file.setData(data);
We use the File entity in a lot of places in our code, and every time we write a query to fetch a File we have to call the web service method to load its data. Also we have to do this for other entities that have a mapping to the File class like below:
#Entity
#Table(name="PATIENT_FILE")
PatientFile {
...
#ManyToOne
#JoinColumn(name="FILE_ID")
File file;
...
}
Now we have to manually fetch the binary data of the file from the web service whenever we load a PatientFile from the database.
Is there a way to have Hibernate do this automatically so that whenever a File entity is loaded, Hibernate fetches its data from the web service to populate the data field of the File object?
NOTE: I know I can implement the Lifecycle interface in the File class and override the onLoad method to do this, but I need a more centralized solution. Plus it wouldn't be nice to call a data layer method from the model.

Yes, you can implement a Hibernate Interceptor. By implementing the onLoad method, which is called just before object initialization, you can make your web service call and populate the data. The method will have to check that it is a File object that you are loading.

Related

Share a org.eclipse.microprofile.graphql.GraphQLApi in a JAR File

I'm writing a GraphQL api in java. I would like to provide this GraphQL api in a JAR file that this api implementation could be consumed/reused in other Java EE applications running in OpenLiberty 22. This is my api implementation.
import org.eclipse.microprofile.graphql.Description;
import org.eclipse.microprofile.graphql.GraphQLApi;
import org.eclipse.microprofile.graphql.Name;
import org.eclipse.microprofile.graphql.NonNull;
import org.eclipse.microprofile.graphql.Query;
...
#GraphQLApi
#RequestScoped
public class SystemStatusGraphQL {
#Inject
private DbAdapter databaseAdapter;
#Query("system")
#NonNull
#Description("Gets status information about the system")
public SystemStatus getSystemStatus(#Name("name") String name) {
return database.getCurrentStatus(name);
}
}
I deployed this JAR file as maven package and consumed it in my target application and I have two seperate problems or questions now.
How to reuse this API so that #GraphQLApi is recognized by OpenLiberty? I tried to inherit from the api class but OpenLiberty does not load GraphQL endpoint.
public class MySystemStatusGraphQL extends com.test.stystem.status.api.SystemStatusGraphQL {
}
When I paste all the GraphQL stuff provided at the top and only try to reuse the model class SystemStatus, Jandex can not resolve object types. When starting OpenLibertyServer, this error occurs: Class [com.mylib.system.status.database.model.SystemStatus] is not indexed in Jandex. Can not scan Object Type, might not be mapped correctly. This error retains even if I create jandex index file on build and add it as part of the JAR file. The SystemStatus class contains all type definitions as you can see:
#Type("SystemStatus")
#Description("Describes current state of system.")
public class SystemStatus{
#NonNull
#Name("_id")
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#NonNull
#Name("serial")
private String serial;
public String getSerial() {
return this.serial;
}
}
I would prefer to reuse whole api which brings the issue mentioned in question 1. If this is not possible, how can I solve issue mentioned in question 2?

Domain Object Returned from RestController causes AJAX error; Custom Model Object works

I have an issue in my SpringMVC RestController that writes JSON results:
1) When I return a domain object as below, ActivitiesT, I get an AJAX 500 Internal Server Error on the client-side.
#RequestMapping("/participant/activityForEvent")
public ActivitiesT getActivityForGuiEventId() throws Exception {
ActivitiesT activitiesT = participantService.getActivity();
return activitiesT;
}
ActivitiesT Domain object, Hiberate-generated:
#Entity
#Table(name = "activities_t", schema = "public")
public class ActivitiesT implements java.io.Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private int id;
//... etc...
}
2) But when I return a custom POJO, the AJAX method works. It's almost the same.
#RequestMapping("/participant/activityForEvent")
public ActivitiesT getActivityForGuiEventId() throws Exception {
ActivitiesT activitiesT = participantService.getActivity();
// Create a custom Activity POJO and return it
return new Activity(activitiesT.id, activitiesT.title);
}
Activity custom POJO:
public class Activity implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
public int id;
public String title;
//etc.
ERROR MESSAGE
HTTP Status 500 - Could not write JSON: could not initialize proxy - no Session; nested exception is com.fasterxml.jackson.databind.JsonMappingException: could not initialize proxy -
no Session (through reference chain: com.myapp")
Some things to note:
Both objects implement Serializable, that's fine
The issue is not DAO, I can see the DAO always returns correct data for both, we can ignore the DAO piece
The Controller is annotated with #RestController, so it always outputs JSON; I don't need a ResponseBody or the older tags
Any thoughts on where the issue may lie? The problem boils down to this:
I can't return JSON from RestController methods on Hibernate-generated #Entity POJOs,
But I can return JSON from RestController methods on my own custom POJOs
When Jackson prepares response, it recursively tries to convert model classes to JSON.For example if you have a Department and Employee relationship with one to many relationship then Jackson executes getEmployees() method in Department.java to prepare the response.But getEmployees() method requires hibernate to execute another query to fetch employee records from DB due to Lazy loading. But the session has been already closed. Hence you get this error.
To avoid this, always use DTO instead of actual model classes.And the same thing you have mentioned that - Domain Object Returned from RestController causes AJAX error; Custom Model Object works.

#PostPersist doesn't save the Audit Record to the DB

I have a Listener with #PostPersist method called "doAudit" to audit create action.On debugging I see This method gets called and Audit record is created on JAVA side. But when I verify the DB I don't see the record.
public class AuditListener {
#PostPersist
public void doAudit(Object object) {
AuditDao auditManager = AuditDaoImpl.getInstance();
auditManager.logEvent("create", object);
}
}
public interface AuditDao {
#Transactional(propagation= Propagation.REQUIRED)
public AuditEntity logEvent(String action, Object object);
}
#Component
public class AuditDaoImpl implements AuditDao {
private static AuditDaoImpl me;
public AuditDaoImpl() {
me = this;
}
public static AuditDaoImpl getInstance() {
return me;
}
#Autowired
private AuditDao dao;
#Transactional
#Override
public AuditEntity logEvent(String action, Object object) {
AuditEntity act = new AuditEntity();
act.setAction(action);
act.setObject(object);
dao.create(act);
return act;
}
}
I am using Open JPA 2.0 as for my ORM. Deployed on karaf container. I am using Postgres SQL as my backend.
Add a debug breakpoint and check if the current stack-trace contains the TransactionInterceptor. If there's no such entry, the Spring transaction management is not properly configured and your DAOs don't use transactions at all.
JPA allows you to run queries without configuring transactions explicitly. For saving data, transactions are mandatory.

From request object to the database

I have an app with an AngularJS front-end and a Spring MVC back-end. I'm having some trouble with converting/mapping request objects to domain/dto objects.
On one page you can add a new order to the system, the POST payload would look something like this:
{
memo: "This is some extra info for order",
orderLines: [{productId:3, quantity:4}, {productId:2, quantity:5}, {productId:1, quantity:4}],
shippingDate: "2014-10-08T19:16:19.947Z",
warehouseId: 2
}
The Spring MVC controller method looks like this:
#RequestMapping(value = "/order", method = RequestMethod.POST)
public ResponseEntity<Void> addOrder(#RequestBody #Valid OrderRequest orderRequest, UriComponentsBuilder b) throws Exception {
// the magic
}
Where OrderRequest is filled with the values of the POST request, the OrderRequest and OrderLineRequest look like this:
public class OrderRequest {
private Long id;
private Date shippingDate;
private String memo;
private List<OrderLineRequest> orderLines;
private Long warehouseId;
public OrderRequest() {
}
// getters and setters ommitted
}
public class OrderLineRequest {
private Long id;
private String productCode;
private int quantity;
public OrderLineRequest() {
}
}
My question now is, in order to save an Order object with orderService.add(order) I need to construct the Order object based on the values that were sent in the request. Where/how do I do this?
OPTION 1
The OrderRequest class could have a makeOrder() method with just returns an Order object like so:
public Order makeOrder() {
Order order = new Order();
order.setMemo(this.memo);
order.setShippingDate(this.shippingDate);
...
}
Then I'd have to map the OrderLineRequest which could have their own makeOrderLine method:
public OrderLine makeOrderLine() {
OrderLine orderLine = new OrderLine();
orderLine.setQuantity = this.quantity;
...what to do with only the productId?
}
As you can see I can set the quantity but in the request I only received the productId, but in the database I save the productCode, productName as well, so I need that info from the database, but I don't want to make a database call from the Request object...I also don't want to half of the mapping in the request object and the rest of the mapping in the controller where I do have access to the services.
OPTION 2
I can use Dozer to do the mapping for me, but that would mean injecting the services into the Dozer custom converters which seem equally unclean to me...
OPTION 3
I pass the OrderRequest object to the service layer and let the service layer handle it, but my question would remain, how exactly would the service layer convert it, say you have the method addOrder like this:
public void addOrder(OrderRequest orderRequest) {
}
Would you call another service to convert from one to the other as I don't really want this conversion in a business logic method?
Any help would be appreciated
use the #RequestBody to map your jsonObject that is send with the request , to a DTO .
please refer to the following tutorial .
hope that helps .
and please ask if there is something not clear .

GWT : SerializationException

I'm trying to use GWT + Spring + Hibernate
When lunshing the application I get this error:
com.google.gwt.user.client.rpc.SerializationException: Type 'org.hibernate.collection.PersistentBag' was not included in the set of types which can be serialized by this SerializationPolicy or its Class object could not be loaded. For security purposes, this type will not be serialized.: instance = [com.asso.shared.model.Activite#64d6357a]
after using this method with the lists of the persistence classes:
public static <T> ArrayList<T> makeGWTSafe(List<T> list) {
if(list instanceof ArrayList) {
return (ArrayList<T>)list;
} else {
ArrayList<T> newList = new ArrayList<T>();
newList.addAll(list);
return newList;
}
}
with my lists I got this:
com.google.gwt.user.client.rpc.SerializationException: Type 'org.hibernate.collection.PersistentBag' was not included in the set of types which can be serialized by this SerializationPolicy or its Class object could not be loaded. For security purposes, this type will not be serialized.: instance = [com.asso.shared.model.Personne#75a2fb58]
==========================================
I have searched in the other subjects but I can't find any solution!
How can I solve this serialization thing!?
I'm using List in my Persistence classes
You need to send DTO object to client side (instead of original one backed by Hibernate). The problem is that your Personne object is actually a Hibernate proxy. Each time when you call some method on it Hibernate do some work (fetch collections from DB for example). There is no simple way to serialize such kind of objects.
Hibernate entities:
//Hibernate entity
public class Personne {
private String name;
private List<Address> addresses;
}
//Hibernate entity
public class Address {
}
Corresponding DTO objects:
public class PersonneDto {
private String name;
private List<AddressDto> addresses;
}
public class AddressDto {
}
Instead of sending Personne to client side you need to create new PersonneDto object, copy state to it and then send to UI. Personne cannot be used in client side because Personne.getAddresses() in most cases hit DB to fetch data (which is inpossible to do in client side JS). So each Personne must be replaced by PersonneDto on client side. As a downside you need to mantnain additional layaer of DTO objects and corresponding code to transform entities to DTOs. There are another approaches to this problem. See this article for more details.

Resources