I'm playing with Quarkus and trying to build a CRUD REST application; I'm trying to get 2 endpoints returning 2 different views of the same entities. Here is an example on how I would have done in Spring + Jackson:
#Entity
public class Car{
public String model;
#ManyToOne( fetch = FetchType.LAZY, cascade = {CascadeType.ALL})
public Owner owner;
// [...]
}
#Entity
public class Owner{
public String name;
// [...]
}
Here it is the important part: now if I were using Jackson I would have create a CarView class:
public class CarView {
public static class Public {};
public static class Private extends Public {};
}
And with that I would have annotated Car.model with #JsonView(CarView.Public.class) and Car.owner with #JsonView(CarView.Private.class) and then just annotate with the same annotations my methods in the REST controller in order to tell Jackson which view I want to use:
#RequestMapping("/car/{id}")
#JsonView(CarView.Public.class)
public Car getPublic(#PathVariable int id) { /*...*/ }
#RequestMapping("/car/private/{id}")
#JsonView(CarView.Private.class)
public Car getPrivate(#PathVariable int id) { /*...*/ }
Can I accomplish the same result using Quarkus & JSON-B?
Quarkus supports usage of JsonViews to manage the serialization/deserialization of request/response.
(Just to let you know, sadly it's not supported (yet) by smallry-openapi implementation, so even if the serialization would work, you'll still see the full model in swagger.)
An example of usage, taken from official guide https://quarkus.io/guides/resteasy-reactive#jsonview-support:
JAX-RS methods can be annotated with #JsonView in order to customize the serialization of the returned POJO, on a per method-basis. This is best explained with an example.
A typical use of #JsonView is to hide certain fields on certain methods. In that vein, let’s define two views:
public class Views {
public static class Public {
}
public static class Private extends Public {
}
}
Let’s assume we have the User POJO on which we want to hide some field during serialization. A simple example of this is:
public class User {
#JsonView(Views.Private.class)
public int id;
#JsonView(Views.Public.class)
public String name;
}
Depending on the JAX-RS method that returns this user, we might want to exclude the id field from serialization - for example you might want an insecure method to not expose this field. The way we can achieve that in RESTEasy Reactive is shown in the following example:
#JsonView(Views.Public.class)
#GET
#Path("/public")
public User userPublic() {
return testUser();
}
#JsonView(Views.Private.class)
#GET
#Path("/private")
public User userPrivate() {
return testUser();
}
When the result the userPublic method is serialized, the id field will not be contained in the response as the Public view does not include it. The result of userPrivate however will include the id as expected when serialized.
Have you checked #JsonbVisibility or "Jsonb adapter" part in
https://javaee.github.io/jsonb-spec/users-guide.html annotation from Jsonb? I am afraid maybe there isn't a solution in Jsonb yet like #JsonView in Jackson. Jsonb adapter is configuration at bean level(you choose the Jsonb instance when you (de)serialize), not at view level.
Related
I'm a little bit confused about using projections in Spring Data JPA.
I wanted to optimize my queries by requesting only needed columns (preferably) in one query, and I thought that using projections is a good idea. But it seems that projection with nested projection becomes open and requests all columns and further nesting is impossible.
I've tried to find a solution with #Query (cannot find how to map nested lists), #EntityGraph (cannot find how to request only specified column) and #SqlResultSetMapping (cannot find how to make mapping nested lists), but it hasn't worked for me.
Is there any solution except receiving List<Object[]> and manually mapping?
I have the next entities classes (simplified for the question):
public class TestAttempt{
private Long id;
private User targetUser;
private Test test;
}
public class Test{
private Long id;
private String name;
private Set<Question> questions;
}
public class Question{
private Long id;
private String name;
private Test test;
}
And I wanted to write something like this (it can be just TestAttempt with null in unused fields):
public interface TestAttemptList {
Long getId();
Test getTest();
interface Test {
String getName();
List<Question> getQuestions();
interface Question {
String getName();
}
}
}
public interface TestAttemptRepository extends JpaRepository<TestAttempt, Long> {
List<TestAttemptList> getAllByTargetUserId(Long targetUserId);
}
And in result get something like this:
{
id: 1,
test: {
name: test1,
questions: [{
name: quest1
}, {
name: quest2
}]
}
}
Ive done something like this... You'll have your repository interfaces which will extend CrudRepository et. al. with the full objects (TestAttempt etc) You define your projections separately. The projection interfaces can contain other projection interfaces (TestAttemptSummary can contain a TestSummary) When the projection interface is used within the given repository the defined methods are applied to the object type the repository is configured for. Something like this.
public interface TestAttemptSummary {
Long getId();
TestSummary getTest();
}
public interface TestSummary {
String getName();
List<QuestionSummary> getQuestions();
}
public interface QuestionSummary {
String getName();
}
public interface TestAttemptRepository extends CrudRepository<TestAttempt, Long> {
TestAttemptSummary getTestAttemptSummary();
}
I'm working on a rest api project with spring boot and hibernate, and I'm wondering on json serialization of RestController using Jackson.
Here is the problem: I use external hibernate entities class defined in a library I cannot edit. This classes are very complex and define lot of field I'm not interested in when I return the object with the rest api.
Actually, I've solved the problem wrapping the original class with a wrapper class that exposes only the values I want to return from the controller.
Eg:
original class
class AccountEntity {
///...
public String getName() {
return this.name;
}
/// ... Lot of code here
}
Wrapper class:
class AccountWrapper {
AccountEntity original;
public AccountWrapper(AccountEntity original) {
this.original = original;
}
public String getName() {
return this.original.getName();
}
}
and the use the Wrapper as following
#RestController("/api/user")
public class UsersController {
#GetMapping("/")
public AccountWrapper getUser() {
AccountEntity account = //get account in some way
AccountWrapper accountWrapper = new AccountWrapper(account);
return accountWrapper;
}
}
The method works well, but it's not very clean and makes stuff more complex (e.g., when I have to return lists), because I always have to wrap the original class.
I didn't found a method to make me able to specify which fields I want to serialize without modify (and I cannot) the original class.
Any help?
Instead of using a wrapper class, create a DTO object for the rest API that will be leaner than the DB entity and a trasformer to create DTO from entity (and vice a verce)
The difference from using a wrapper here is that the DB entity is not part of the DTO, and thus does not need to be serialized on the response.
The big advantage here is that you separate the DB layer from the API layer, which makes it more flexible and easy to manage.
you can read more about this pattern here
Apparently, you can use Jackson Mixins to annotate a class with Jackson annotations.
See this answer for example.
The idea is to create an class with the annotations you want and to use objectMapper.getSerializationConfig().addMixInAnnotations() to register the MixIn with your class.
For example:
//Class you don't controll
public class User {
private String name;
private String password; //attribute we want to omit
//... getters and setters
}
public abstract class UserMixIn {
#JsonIgnore String getPassword();
}
objectMapper.addMixInAnnotations(User.class, UserMixIn.class);
Hope it helps,
I'm using Spring Data REST to build my application. It's been working very well so far, but I'd like to add some customizations to returned entity while still keeping the automatically generated links.
I'd like to do something like this:
#RepositoryRestController
public class SomeController {
#GetMapping("/entity/{id}")
public SomeEntity getEntity(#PathVariable int id)
SomeEntity entity = SpringDataREST.findById(id); //-> is there a way to do this?
Link randomLink = generateRandomLink();
entity.addLink(randomLink);
//do other stuff with entity
return entity;
}
}
Where SomeEntity class extends Spring HATEOAS ResourceSupport.
If you are using Spring Data REST you can use RepositoryEntityLinks to programmatically create links:
#Component
public class MyBean {
private final RepositoryEntityLinks entityLinks;
public MyBean(RepositoryEntityLinks entityLinks) {
this.entityLinks = entityLinks;
}
public Link someMethod(MyEntity entity) {
//...
return entityLinks.linkToSingleResource(entity)
}
}
Note - to use linkToSingleResource method, the MyEntity must implement Identifiable interface. Instead you can use method linkForSingleResource:
return entityLinks.linkForSingleResource(MyEntity.class, entity.getId())
Suppose you have this entity:
class Foo{
String propA;
String propB;
}
and you want to serialize for one API like :
{propA: "ola",
propB: "Holla"}
and for another API like :
{fooPropA: "ola",
fooPropB: "Holla"}
How can this be achieved using jackson and using the same entity. Creating 2 different entities is not an option :)
There are several ways in which you can achieve this. You can enable a custom serializer (already covered by #se_vedem), register an annotation introspector which changes the property names for the corresponding class and so on.
However, if you are willing to only add a string prefix to all the property names, then the Jackson property name strategy is probably the best fit. The naming strategy class has the access to the serialized object type information, so you can make a decision whether to change the property name or not.
Here is an example using a custom annotation that defines the prefix:
public class JacksonNameStrategy {
#Retention(RetentionPolicy.RUNTIME)
public static #interface PropertyPrefix {
String value();
}
#PropertyPrefix("foo_")
public static class Foo {
public String propA;
public String propB;
public Foo(String propA, String propB) {
this.propA = propA;
this.propB = propB;
}
}
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(new MyPropertyNamingStrategyBase());
System.out.println(mapper.writeValueAsString(new Foo("old", "Holla")));
}
private static class MyPropertyNamingStrategyBase extends PropertyNamingStrategy {
#Override
public String nameForField(MapperConfig<?> config,
AnnotatedField field,
String defaultName) {
PropertyPrefix ann = field.getDeclaringClass().getAnnotation(PropertyPrefix.class);
if (ann != null) {
return ann.value() + defaultName;
}
return super.nameForField(config, field, defaultName);
}
}
}
Output:
{"foo_propA":"old","foo_propB":"Holla"}
In your API method you choose between two ObjectMapper instances one with the default naming naming strategy and one with the custom one.
You can achieve this by using modules feature from Jackson.
Basically, each API would have it's own ObjectMapper and they will be configured with different modules. This way you can create 2 serializers for the same class and register them on the appropriate module. More read can be found here http://wiki.fasterxml.com/JacksonFeatureModules
However, be aware that serializers are loaded in a particular order. First it tries to get the annotated ones, if none is found it will try to get those registered from modules. So, for example if you have your class annotated with serializer, then that serializer(FooSerializer) would be chosen instead of the one configured in module(MySecondFooSerializer).
#JsonSerialize(using = FooSerializer.class)
class Foo{
String propA;
String propB;
}
module.addSerializer(Foo.class, new MySecondFooSerializer());
I have an entity named Commercial. I have an Category entity where the list of commercial categories are hold. For each category there is an separate entity extending Commercial(like RestaurantCommercial, PhotoStudioCommercial etc. total up to 20 entities) with JOINED inheritance strategy.
Commercial entity holds up general properties like title, text contactnumber of some company's commercial, while RestaurantCommercial and PhotoStudioCommercial holds additional specific properties concerned with that category.
The problem is that writing a separate dao and controller for each entity is a bit plenty of work, so I am searching for a neat way to handle this issue.
I need an unified controller and may be the DAO for handling the form control and persisting new instances of the entities that extend Commercial.
Here is approximately what I was thinking about:
#RequestMapping(value={"/vendor/commercial/{categoryName}/new"}, method=RequestMethod.GET)
public String showNewCommercialForm(#PathVariable("categoryName") String categoryName,
Map<String, Object> map) {
Category category = categoryDAO.getCategoryByServiceName(categoryName);
Class clazz = Class.forName(category.getClassName()); //here className is smth like domain.commercial.RestaurantCommercial
map.put("commercialInstance", clazz.newInstance());
//separate view for each category of commercial
return "vendor/commercial/"+categoryName+"/new";
}
And I was thinking for a similar controller for saving form data even if I would have to write a sperate binder for this stuff.
So the question is: What would you suggest to handle this issue or what would be the best practice if you had already faced similar need(Generics, Reflection or smth else)? Or if that would be worthy or not and why?
Thanks in advance.
I create a Dao interface for such cases:
public interface Dao<T extends Commercial> {
T getAll();
}
After that an abstract Dao implementation, for example hibernate based:
public CommonHibernateDao<T extends Commercial> implements Dao<T>
{
private final Class<T> entityType;
protected CommonHibernateDao(Class<T> entityType) {
this.entityType = entityType;
}
public List<T> getAll() {
// hibernate get all implementation
}
}
And RestaurantCommercial Dao interface and implementation:
public interface RestaurantCommercialDao extends Dao<RestaurantCommercial> {
}
public class HibernateRestaurantCommercialDao extends CommonHibernateDao<RestaurantCommercial> implements RestaurantCommercialDao {
public HibernateRestaurantCommercialDao() {
super(RestaurantCommercial.class);
}
}
All implementation goto CommonHibernateDao. In it's subclasses only constructor calling neaded. Basically you can do it with reflection but as for me it is not clear.
For controller (something like RESTfull API):
#Controller
public class YourController() {
#RequestMapping(value = "/restapi/{entityType}")
public String postEntity(HttpServletRequest request, #PathVariable(value = "entityType") String entityType) {
// If enetity name will be packegae + class name
Object beanInstance = Class.forName(entityName);
ServletRequestDataBinder binder = new ServletRequestDataBinder(beanInstance);
binder.bind(request);
// Here by type of entity you can get DAO and persist
}
}
If form input names will be same to your bean names - binding will do all automatically.