I am currently writing a service where I can store a geospatial point with some data. I have a "dataPoint" class that looks like this:
#Entity
#Table(name = "datapoint")
public class DataPoint {
#Id
int dataPoint_id;
#Column(name = "body")
String body;
#Column(name = "location", columnDefinition = "Geometry")
PGgeometry location;
#Column(name = "deleted")
boolean deleted;
//Getters and Setters...
I am trying to use Spring Boot to simply add a point with some information to a PostGIS database via an API path. I have built a controller that looks like this:
#RestController
#RequestMapping(value = "/dataPoint")
public class DataPointController {
#Autowired
private DataPointService myPointService;
#RequestMapping(value = "/add/{body}/{latitude}/{longitude}/")
public DataPoint addDataPoint(#PathVariable String body, #PathVariable double latitude, #PathVariable double longitude){
DataPoint myPoint = new DataPoint();
myPoint.setBody(body);
PGgeometry geometry = new PGgeometry();
try {
geometry.setValue("POINT("+longitude +" " + latitude+")");
geometry.setType("POINT");
// Debugging Stuff
System.out.println("GEOMETRY VALUE LOOK: {{{{ " + geometry.getValue() + " " + geometry.getType());
} catch (SQLException e) {
e.printStackTrace();
}
myPoint.setLocation(geometry);
myPointService.saveDataPoint(myPoint);
return myPoint;
}
Which is in turn linked to a DataPointService which just acts as a middle man between the controller where saveDataPoint() looks like this:
public void saveDataPoint(DataPoint myPoint) {
dataPointRepository.save(myPoint);
}
and the DataPointRepository, which looks like this:
#Repository
public interface DataPointRepository extends JpaRepository<DataPoint, Integer> {
}
However, when I visit my add link, I get this error:
Failed to write HTTP message: org.springframework.http.converter.HttpMessageNotWritableException: Could not write content: Direct self-reference leading to cycle (through reference chain: com.testing.model.DataPoint["location"]->org.postgis.PGgeometry["geometry"]->org.postgis.Point["firstPoint"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Direct self-reference leading to cycle (through reference chain: com.testing.model.DataPoint["location"]->org.postgis.PGgeometry["geometry"]->org.postgis.Point["firstPoint"])
I have seen the #JsonBackReference and its dual used in some examples, however, that has been used in situations where entities are being linked back and forth, which I do not see happening here, in fact, the error does not even seem to be cyclic, so what is happening here?
I ran into the same issue. It's cyclic because Point has a field firstPoint that reference to Point again.
I was able to resolve the problem by installing this postgis-geojson: https://jitpack.io/p/stephenbrough/postgis-geojson
Related
I tried get entity by Data JPA & Data Rest without HATEOAS.
The condition is that I use the HATEOAS form, and sometimes I need a pure Json response.
So I'm creating JSON by creating the controller path separately from the repository's endpoint and creating the DTO class separately.
this is my code :
#RepositoryRestController
public class MetricController {
#Autowired
private MetricRepository metricRepository;
#RequestMapping(method = RequestMethod.GET, value = "/metrics/in/{id}")
public #ResponseBody
MetricDTO getMetric(#PathVariable Long id) {
return MetricDTO.fromEntity(metricRepository.getOne(id));
}
}
#RepositoryRestResource
public interface MetricRepository extends JpaRepository<Metric, Long> { }
#Setter
#Getter
#NoArgsConstructor
#AllArgsConstructor
public class MetricDTO {
private SourceType sourceType;
private String metricTypeField;
private String metricType;
private String instanceType;
private String instanceTypeField;
private List<String> metricIdFields;
private List<String> valueFields;
private Map<String, String> virtualFieldValueEx;
public static MetricDTO fromEntity(Metric metric) {
return new MetricDTO(
metric.getSourceType(),
metric.getMetricTypeField(),
metric.getMetricType(),
metric.getInstanceType(),
metric.getInstanceTypeField(),
metric.getMetricIdFields(),
metric.getValueFields(),
metric.getVirtualFieldValueEx()
);
}
}
It's the way I do, but I expect there will be better options and patterns.
The question is, I wonder if this is the best way.
HATEOAS (Hypermedia as the Engine of Application State) is a constraint of the REST application architecture.
It basically tells that anyone who is a consumer of your REST endpoints can navigate between them with the help of the link.
let take your example
**HTTP Method** **Relation (rel)** **Link**
GET Up /metrics/in
GET Self /metrics/in/{id}
GET SourceType /sourceType/{id}
GET metricIdFields /url for each in JSON aarray
Delete Delete /employe/{employeId}
Use org.springframework.hateoas.Links class to create such link in your DTOs.
in you DTO add
public class MetricDTO {
private Links links;
//Getters and setters
//inside your setters add SLEF , GET , create Delete for current resource
}
https://www.baeldung.com/spring-hateoas-tutorial
I've been scanning stack overflow all day and have not come across a viable solution for my problem.
I have a pojo that has primitive types and nested objects. For example...
#JsonIgnoreProperties({"duration", "errorCode", "haveFieldsChanged",
"serviceRequestToken", "storedProcDuration"}) // Abstract Base Class
properties
class Bus extends AbstractBaseClass implements Serializable{
#JsonIgnore
private static final long serialVersionId = 1;
#JsonProperty("name")
String name;
#JsonProperty("id")
int id;
#JsonProperty("students")
List<Student> students; // Nested Objects
#JsonProperty("employer")
Employer employer; //Nested object
// Getters and setters - none are annotated
#JsonRootName(value = "student")
class Student implements Serializable{
// student fields
}
#JsonRootName(value = "statusType")
class Employer implements Serializable{
#JsonProperty("id")
int id;
}
When I serialize my Bus object, jackson has no problem creating the proper structure for name, id, and my list of students. However, it will skip over Employer entirely leaving it absent from the json. See below.
{
"name":"Sean",
"id": 1,
"students":[student objects...]
}
I have tried #JsonProperty, #JsonSerialize(as = Employer.class), I tried building map for the employer object. I feel like I have exhausted most options. Is there something I am missing?
I ran into stack overflow exceptions trying some other annotations. I appreciate any help I can get.
The reason I added #JsonProperty to the fields is to help drive deserialization. I think this may be the root cause for serialization, but I am not certain.
Serialization Implementation
private String serializeBus(Bus bus) throws Exception {
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, Visibility.NONE);
mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
String json = null;
try {
json = mapper.writeValueAsString(bus);
} catch (JsonProcessingException e) {
logger.error("Error serializing bus");
throw new Exception(e);
}
return json;
}
Thank you!
So, if it is not feasible for you to share code, then I put your example above in a little sandbox project: https://github.com/mle-enso/stackoverflow
It currently runs with Spring Boot 2.1 but also downgrading the managed Jackson version(s) to 2.6.1 has no negative impact.
Maybe you could try this project with a simple mvn clean verify or a manual test run of de.mle.stackoverflow.jackson.BusSerializerTest to dig further into this issue here.
I'm currently using Redis (3.2.100) with Spring data redis (1.8.9) and with Jedis connector.
When i use save() function on an existing entity, Redis delete my entity and re create the entity.
In my case i need to keep this existing entity and only update attributes of the entity. (I have another thread which read the same entity at the same time)
In Spring documentation (https://docs.spring.io/spring-data/data-redis/docs/current/reference/html/#redis.repositories.partial-updates), i found the partial update feature. Unfortunately, the example in the documentation use the update() method of RedisTemplate. But this method do not exist.
So did you ever use Spring-data-redis partial update?
There is another method to update entity redis without delete before?
Thanks
To get RedisKeyValueTemplate, you can do:
#Autowired
private RedisKeyValueTemplate redisKVTemplate;
redisKVTemplate.update(entity)
You should use RedisKeyValueTemplate for make partial update.
Well, consider following docs link and also spring data tests (link) actually made 0 contribution to resulting solution.
Consider following entity
#RedisHash(value = "myservice/lastactivity")
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
public class LastActivityCacheEntity implements Serializable {
#Id
#Indexed
#Size(max = 50)
private String user;
private long lastLogin;
private long lastProfileChange;
private long lastOperation;
}
Let's assume that:
we don't want to do complex read-write exercise on every update.
entity = lastActivityCacheRepository.findByUser(userId);
lastActivityCacheRepository.save(LastActivityCacheEntity.builder()
.user(entity.getUser())
.lastLogin(entity.getLastLogin())
.lastProfileChange(entity.getLastProfileChange())
.lastOperation(entity.getLastOperation()).build());
what if there would pop up some 100 rows? then on each update entity got to fetched and saved, quite inefficient, but still would work out.
we don't actually want complex exercises with opsForHash + ObjectMapper + configuring beans approach - it's quite hard to implement and maintain (for example link)
So we're about to use something like:
#Autowired
private final RedisKeyValueTemplate redisTemplate;
void partialUpdate(LastActivityCacheEntity update) {
var partialUpdate = PartialUpdate
.newPartialUpdate(update.getUser(), LastActivityCacheEntity.class);
if (update.getLastLogin() > 0)
partialUpdate.set("lastlastLogin", update.getLastLogin());
if (update.getLastProfileChange() > 0)
partialUpdate.set("lastProfileChange", update.getLastProfileChange());
if (update.getLastOperation() > 0)
partialUpdate.set("lastOperation", update.getLastOperation());
redisTemplate.update(partialUpdate);
}
and the thing is - it doesn't really work for this case.
That is, values getting updated but you can not query new property later on via repository entity lookup: certain lastActivityCacheRepository.findAll() will return unchanged properties.
Here's the solution:
LastActivityCacheRepository.java:
#Repository
public interface LastActivityCacheRepository extends CrudRepository<LastActivityCacheEntity, String>, LastActivityCacheRepositoryCustom {
Optional<LastActivityCacheEntity> findByUser(String user);
}
LastActivityCacheRepositoryCustom.java:
public interface LastActivityCacheRepositoryCustom {
void updateEntry(String userId, String key, long date);
}
LastActivityCacheRepositoryCustomImpl.java
#Repository
public class LastActivityCacheRepositoryCustomImpl implements LastActivityCacheRepositoryCustom {
#Autowired
private final RedisKeyValueTemplate redisKeyValueTemplate;
#Override
public void updateEntry(String userId, String key, long date) {
redisKeyValueTemplate.update(new PartialUpdate<>(userId, LastActivityCacheEntity.class)
.set(key, date));
}
}
And finally working sample:
void partialUpdate(LastActivityCacheEntity update) {
if ((lastActivityCacheRepository.findByUser(update.getUser()).isEmpty())) {
lastActivityCacheRepository.save(LastActivityCacheEntity.builder().user(update.getUser()).build());
}
if (update.getLastLogin() > 0) {
lastActivityCacheRepository.updateEntry(update.getUser(),
"lastlastLogin",
update.getLastLogin());
}
if (update.getLastProfileChange() > 0) {
lastActivityCacheRepository.updateEntry(update.getUser(),
"lastProfileChange",
update.getLastProfileChange());
}
if (update.getLastOperation() > 0) {
lastActivityCacheRepository.updateEntry(update.getUser(),
"lastOperation",
update.getLastOperation());
}
all credits to Chris Richardson and his src
If you don't want to type your field names as strings in the updateEntry method, you can use use the lombok annotation on your entity class #FieldNameConstants. This creates field name constants for you and then you can access your field names like this:
...
if (update.getLastOperation() > 0) {
lastActivityCacheRepository.updateEntry(update.getUser(),
LastActivityCache.Fields.lastOperation, // <- instead of "lastOperation"
update.getLastOperation());
...
This makes refactoring the field names more bug-proof.
I'm currently working on a SpringBoot API to interface with a MongoRepository, but I'm having trouble understanding how the JSON being passed becomes a Document for storage within Mongo. I currently have a simple API that stores a group of users:
#Document
#JsonInclude
public class Group {
#Id
#JsonView(Views.Public.class)
private String id;
#JsonView(Views.Public.class)
private String name;
#JsonView(Views.Public.class)
private Set<GroupMember> groupMembers = new HashSet<>();
}
There are also setter and getter methods for each of the fields, although I don't know how necessary those are either (see questions at the end).
Here is the straightforward component I'm using:
#Component
#Path("/groups")
#Api(value = "/groups", description = "Group REST")
public class Groups {
#Autowired
private GroupService groupService;
#GET
#Produces(MediaType.APPLICATION_JSON)
#ApiOperation(value = "Get all Groups", response = Group.class, responseContainer = "List")
#JsonView(Views.Public.class)
public List<Group> getAllGroups() {
return groupService.getAllGroups();
}
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#ApiOperation(value = "Create a Group", response = Group.class)
#JsonView(Views.Detailed.class)
public Group submitGroup(Group group) {
return groupService.addGroup(group);
}
}
Finally, I have a Service class:
#Service
public class GroupServiceImpl implements GroupService {
#Autowired
private GroupRepository groupRepository;
#Override
public Group addGroup(Group group) {
group.setId(null);
return groupRepository.save(group);
}
#Override
public List<Group> getAllGroups() {
return groupRepository.findAll();
}
}
The GroupRespository is simply an interface which extends MongoRepository<Group,String>
Now, when I actually make a call to the POST method, with a body containing:
{
"name": "group001",
"groupMembers": []
}
I see that it properly inserts this group with a random Mongo UUID. However, if I try to insert GroupMember objects inside the list, I receive a null pointer exception. From this, I have two questions:
How does SpringBoot (Jackson?) know which fields to deserialize from the JSON being passed? I tested this after deleting the getter and setter methods, and it still works.
How does SpringBoot handle nested objects, such as the Set inside the class? I tested with List instead of Set, and it worked, but I have no idea why. My guess is that for each object that is both declared in my class and listed in my JSON object, SpringBoot is calling a constructor that it magically created behind the scenes, and one doesn't exist for the Set interface.
Suppose I'm adamant on using Set (the same user shouldn't show up twice anyway). What tools can I use to get SpringBoot to work as expected?
It seems to me that a lot of the things that happen in Spring are very behind-the-scenes, which makes it difficult for me to understand why things work when they do. Not knowing why things work makes it difficult to construct things from scratch, which makes it feel as though I'm hacking together a project rather than actually engineering one. So my last question is something like, is there a guide that explains the wiring behind the scenes?
Finally, this is my first time working with Spring... so please excuse me if my questions are entirely off the mark, but I would appreciate any answers nonetheless.
I am making a restful application and trying to convert a list of objects into json for a specific url (#RequestMapping / #ResponseBody )
I have jackson-hibernate4 and jackson-core ,databind etc in my classpath.
Here is my object that i want to convert in json.
#Entity
#Table(name="Product")
public class Product {
#Id
#Column(name="productId")
#GeneratedValue
protected int productId;
#Column(name="Product_Name")
protected String name;
#Column(name="price")
protected BigDecimal baseprice;
#OneToMany(cascade = javax.persistence.CascadeType.ALL,mappedBy="product",fetch=FetchType.EAGER)
protected List<ProductOption> productoption = new ArrayList<ProductOption>();
#OneToMany(cascade = javax.persistence.CascadeType.ALL,mappedBy="product",fetch=FetchType.EAGER)
protected List<ProductSubOption> productSubOption = new ArrayList<ProductSubOption>();
#ManyToOne
#JoinColumn(name="ofVendor")
protected Vendor vendor;
The two objects inside Product are also POJO'S..
Here is my method that retrieves the list of product
#Override
public List<Product> getMenuForVendor(int vendorId) {
List<Product> result = em.createQuery("from "+Product.class.getName()+" where ofVendor = :vendorId").setParameter("vendorId", vendorId).getResultList();
System.out.println(result.size());
return result;
}
When i try to return this list in my controller I was getting a "Cannot lazily load for json" so i set my objects to be fetched eagerly.
Here is my controller
#Autowired
private MenuDaoImpl ms;
#RequestMapping(value = "/{vendorId}", method = RequestMethod.GET)
public #ResponseBody List<Product> getMenu(#PathVariable int vendorId){
List<Product> Menu = Collections.unmodifiableList(ms.getMenuForVendor(vendorId));
return Menu;
}
Now when i hit my url localhost:8080/getMenu/1 I should be getting a json string displayed but I get a big list of errors
WARN : org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver - Handling of [org.springframework.http.converter.HttpMessageNotWritableException] resulted in Exception
java.lang.IllegalStateException: Cannot call sendError() after the response has been committed
at org.apache.catalina.connector.ResponseFacade.sendError(ResponseFacade.java:467)
Could not write JSON: Infinite recursion (StackOverflowError) (through reference chain:
I am not sure if I am missing anything. Please guide .
I solved it using #JsonBackReference for #ManyToOne binding and #JsonManagedReference on #OneToMany binding.
Thanks "Sotirios Delimanolis"
The question has already been answered. I am simply putting the link of a good example that clearly explains both the problem and the solution. http://geekabyte.blogspot.in/2013/09/fixing-converterhttpmessagenotwritablee.html
I realize this may not be 100% what you are after, but never the less, I felt like sharing it as I spent a lot of time fighting with this issue back in the days.
Also, instead of using the Json annotations, you could consider a custom Json parser.
Make sure to use the correct package for the Jackson jars, as they recently changed their package structure (when you use any of their classes with the number 2 in it, like the ones below).
Start by creating a HttpMessageConverter:
#Bean
public HttpMessageConverter jacksonMessageConverter() {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setPrefixJson(false);
converter.setPrettyPrint(true);
converter.setObjectMapper(objectMapper());
return converter;
}
Add a ObjectMapper where you attach a mapping module and attach the serializers you will use.
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
SimpleModule module = new SimpleModule("jacksonJsonMapper", Version.unknownVersion());
module.addSerializer(Product.class, new Product());
objectMapper.registerModule(module);
return objectMapper;
}
Now create a serializer. This class will provide the output you see when you fetch objects and Jackson will do the rest. You just provide the skeleton of how it should look.
public class Product erializer extends JsonSerializer<Product> {
#Override
public void serialize(Product product, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
if(product == null) {
//Handle it, if you want
}
if(product != null) {
jsonGenerator.writeStartObject();
jsonGenerator.writeStringField("id", productId.getId().toString());
jsonGenerator.writeStringField("title", product.getName());
jsonGenerator.writeStringField("basePrice", product.getBasePrice());
//Add items to the json array representation
jsonGenerator.writeArrayFieldStart("productoptions");
for(ProductOption productOption: product.getProductoption()) {
jsonGenerator.writeStartObject("field", productOption.getFoo());
jsonGenerator.writeEndObject();
}
jsonGenerator.writeEndArray();
jsonGenerator.writeEndObject();
}
}
}
On a side note, but one which I still hope will be useful:
You need to make sure you have a transaction available when fetching entities lazily. You also should keep in mind that lazy is the preferable way of loading entities, unless you want to utterly crush your server every time you fetch any reference.
Try to change the method that fetch data by adding #Transactional on top of it, to make sure there is a transaction open for the method while its running, if not it will likely be closed by the time you try to fetch child objects.