Transportation layer that support Polymorphism and Generics - ajax

I am looking for transportation layer for gwt. I would like to create ajax request using generic method, f.e this is my DAO/service:
public class GenericDao<T extends GenericModel<T>> {
private Logger logger = LoggerFactory.getLogger(this.getClass().getCanonicalName());
#Transient protected Class<T> entityClass;
public GenericDao() {
super();
}
public GenericDao(Class<? extends GenericModel<T>> clazz) {
this.entityClass = (Class<T>) clazz;
}
public T getBy(Long id) {
return JPA.em().find(entityClass, id);
}
public List<GenericModel<T>> get() {
logger.error("trying to get data from db");
return getList();
}
public List<GenericModel<T>> getList() {
return JPA.em().createQuery("FROM " + entityClass.getSimpleName()).getResultList();
}
public void save(GenericModel<T> entityClass) {
JPA.em().getTransaction().begin();
JPA.em().persist(entityClass);
JPA.em().getTransaction().commit();
}
public void update(T entityClass) {
JPA.em().getTransaction().begin();
JPA.em().merge(entityClass);
JPA.em().getTransaction().commit();
}
public void delete(T entityClass) {
JPA.em().getTransaction().begin();
JPA.em().remove(entityClass);
JPA.em().getTransaction().commit();
}
}
GenericModel/Entity:
#MappedSuperclass
public class GenericModel<T extends GenericModel<T>> implements Identifiable, Versionable {
#Transient
protected Class<T> entityClass;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Version
private Integer version;
// setter & getter
#Override
public Long getId() {return id;}
public void setId(Long id) {this.id = id;}
#Override
public Integer getVersion() {return version;}
public void setVersion(Integer version) {this.version = version;}
// constructor
public GenericModel() {
Class<?> obtainedClass = getClass();
Type genericSuperclass = null;
for (;;) {
genericSuperclass = obtainedClass.getGenericSuperclass();
if (genericSuperclass instanceof ParameterizedType) {
break;
}
obtainedClass = obtainedClass.getSuperclass();
}
ParameterizedType genericSuperclass_ = (ParameterizedType) genericSuperclass;
try {
entityClass = ((Class) ((Class) genericSuperclass_
.getActualTypeArguments()[0]));
} catch (ClassCastException e) {
entityClass = guessEntityClassFromTypeParametersClassTypedArgument();
}
}
public GenericModel(Long id) {
this();
this.id = id;
}
}
I am looking for mechanism that will allow me to use this generic service for all models on client side (each db entity have id- so I would like to downloads using ajax all my Entities this way, so I should have only one generic method for that on client side).
I've already checked:
GWT-RPC
RequestFactory
RestyGWT
But none of them support this feature.
I've found here:
https://www.mail-archive.com/google-web-toolkit#googlegroups.com/msg100095.html
information that: gwt-jackson supports generics and polymorphism. Unfortunately I didn't found any working example that. Can someone help, give an example, approved that information?
All entities have id and version parameter. So I would like to have one metod on client side RF that will allow me to get from server(service/dao/whatever) that entity by id- like this: Request getBy(Long id); But unfortunatelly I can't make it work. I like the RF way, so I've tried it first. Generally I don't wonna repeat code for downloading entity/proxy by id.
For better understanding, please look also on:
RequestFactory client-side inheritance of typed class with generics

I'm confused as to why you think RPC can't handle generics - according to your link, it can, but RestyGWT cannot. Granted, none of your JPA references make any sense in GWT, but those would live in a DAO on the server, not in the entity/model class themselves, or at least not in the client version. If you had a RPC method that returned T where <T extends GenericModel<T>>, then you would have serializers for every possible GenericModel<?> subtype, and any/all that are gwt-compatible could be sent over the wire.
Edit from update to question:
Your GenericModel class uses features of Java that cannot work in GWT, such as reflection. This cannot be compiled to GWT, since the compiler relies on removing reflection information to minimize your compiled size - leaving in general reflection information means leaving in details about all classes and members, even ones that it can't statically prove are in use, since some reflection might make use of them.
If there is a way to phrase your model object in a way that just deals with the data at hand, focus on that. Otherwise consider a DTO which is just the data to send over the wire - I'm not sure how you would plan to use the entityClass field on the client, or why that would be important to read from the superclass's generics instead of just using getClass().
RequestFactory will have a hard time dealing with generics - unlike RPC (and possibly RestyGWT) it cannot handle polymorphism the way you want, but will instead only send the fields for the declared type, not any arbitrary subtype. RPC will actually send the instance if it is something that the client can handle.

Related

Two-way binding in Android with data from Room database

I am new to the MVVM architecture in Android, and I have some days with a doubt that I consider basic, but that I can't solve.
I proceed to discuss my problem:
I have an Entity, CustomerView (this entity is created from a DatabaseView):
#DatabaseView("select ... ")
public class CustomerView {
public String cardCode;
public String cardName;
public String cardFName;
...
Then, I have a Dao class:
#Dao
public interface OCRD_DAO {
...
#Query("SELECT * from CustomerView where cardCode= :cardCode")
LiveData<CustomerView> getCustomerViewByCardCode(String cardCode);
...
}
The repository class, makes use of the DAO class:
public LiveData<CustomerView> getCustomer(String cardCode){
return mOcrdDao.getCustomerViewByCardCode(cardCode);
}
The CustomerSheetViewModel class:
public class CustomerSheetViewModel extends BaseObservable {
private Repository mRepository;
public LiveData<CustomerView> mCustomer;
private MutableLiveData<String> _cardName;
#Bindable
public MutableLiveData<String> getCardName(){
return this._cardName;
}
public MutableLiveData<String> setCardName(String value){
// Avoids infinite loops.
if (mCustomer.getValue().cardName != value) {
mCustomer.getValue().cardName = value;
// React to the change.
saveData();
// Notify observers of a new value.
notifyPropertyChanged(BR._cardName);
}
}
public CustomerSheetViewModel (Application application, String cardCode) {
mRepository = new Repository(application);
this.mCustomer = mRepository.getCustomer(cardCode);
//Init MutableLiveData????
this._cardName = this.mCustomer.getValue().cardName;
//Null Exception, because this.mCustomer.getValue() is null
}
}
At this point, my problem occurs: when I initialise the CustomerView object, it is of type LiveData. However, if I want to make use of 2-way binding, I need an object of type MutableLiveData. So, I think I should create the MutableLiveData object with the data extracted from the database (i.e. from the call to the repository). When I try this (e.g. getValue().cardName) a null exception is thrown, since LiveData is asynchronous.
Finally, I could make use of this property in the layout:
android:text="#={customerSheetViewModel.cardName}"
I really appreciate any help, as I can't find any reference to 2-way binding when the data comes from a database read.
Thanks in advance.

Repository vs. DAO (again)

In general this back-story does not matter but just to explain the code below:
The server handles users and user groups. User groups are able to "discover" places - at this point in time these places are coming exclusively from the Google Places API.
Current Implementation
Currently, I have a lot of JpaRepository objects, which I call Repository, in my Service Layer. I am stressing "Repository" because in my proposed solution below, they'd be downgraded to DAOs.
However, what I do not like in my current code, and also the reason for my question here, is the amount of repositories one can find in the UserGroupService.
#Service
public class UserGroupService {
private final static Logger LOGGER = LogManager.getLogger(UserGroupService.class);
#Autowired
private UserGroupRepository userGroupRepository;
#Autowired
private UserGroupPlaceRepository userGroupPlaceRepository;
#Autowired
private PlaceRepository placeRepository;
#Autowired
private GooglePlaceRepository googlePlaceRepository;
#Autowired
private GooglePlaces googlePlaces;
public UserGroupService() {
}
#Transactional
public void discoverPlaces(Long groupId) {
final UserGroup userGroup = this.userGroupRepository.findById(groupId).orElse(null);
if (userGroup == null) {
throw new EntityNotFoundException(String.format("User group with id %s not found.", groupId));
}
List<PlacesSearchResult> allPlaces = this.googlePlaces.findPlaces(
userGroup.getLatitude(),
userGroup.getLongitude(),
userGroup.getSearchRadius());
allPlaces.forEach(googlePlaceResult -> {
GooglePlace googlePlace = this.googlePlaceRepository.findByGooglePlaceId(googlePlaceResult.placeId);
if (googlePlace != null) {
return;
}
Place place = new Place();
place.setLatitude(googlePlaceResult.geometry.location.lat);
place.setLongitude(googlePlaceResult.geometry.location.lng);
place.setPlaceType(Place.PlaceType.GOOGLE_PLACE);
place.setName(googlePlaceResult.name);
place.setVicinity(googlePlaceResult.vicinity);
place = this.placeRepository.save(place);
UserGroupPlace.UserGroupPlaceId userGroupPlaceId = new UserGroupPlace.UserGroupPlaceId();
userGroupPlaceId.setUserGroup(userGroup);
userGroupPlaceId.setPlace(place);
UserGroupPlace userGroupPlace = new UserGroupPlace();
userGroupPlace.setUserGroupPlaceId(userGroupPlaceId);
this.userGroupPlaceRepository.save(userGroupPlace);
googlePlace = new GooglePlace();
googlePlace.setPlace(place);
googlePlace.setGooglePlaceId(googlePlaceResult.placeId);
this.googlePlaceRepository.save(googlePlace);
});
}
}
A Solution That Does Not Work
What could make this code a lot simpler and had the potential to resolve this mess up there, would be #Inheritance:
#Entity
#Table(name = "place")
#Inheritance(strategy InheritanceType.JOINED)
public class Place { /* .. */ }
#Entity
#Table(name = "google_place")
public class GooglePlace extends Place { /* .. */ }
However, this is not an option because then I cannot have a PlaceRepository which saves just a place. Hibernate does not seem to like it..
My proposal
I think my confusion starts with the names that Spring is using. E.g. JpaRepository - I am not so sure if this is actually "the right" name. Because as far as I understood, these objects actually work like data access objects (DAOs). I think it should actually look something like this:
public interface PlaceDao extends JpaRepository<Place, Long> {
}
public interface GooglePlaceDao extends JpaRepository<Place, Long> {
}
#Repository
public class GooglePlaceRepository {
#Autowired
private PlaceDao placeDao;
#Autowired
private GooglePlaceDao googlePlaceDao;
public List<GooglePlace> findByGroupId(Long groupId) {
// ..
}
public void save(GooglePlace googlePlace) {
// ..
}
public void saveAll(List<GooglePlace> googlePlaces) {
// ..
}
}
#Service
public class UserGroupService {
#Autowired
private GooglePlaceRepository googlePlaceRepository;
#Autowired
private UserGroupRepository userGroupRepository;
#Transactional
public void discoverPlaces(Long groupId) {
final UserGroup userGroup = this.userGroupRepository.findById(groupId).orElse(null)
.orElseThrow(throw new EntityNotFoundException(String.format("User group with id %s not found.", groupId)));
List<PlacesSearchResult> fetched = this.googlePlaces.findPlaces(
userGroup.getLatitude(),
userGroup.getLongitude(),
userGroup.getSearchRadius());
// Either do the mapping here or let GooglePlaces return
// List<GooglePlace> instead of List<PlacesSearchResult>
List<GooglePlace> places = fetched.stream().map(googlePlaceResult -> {
GooglePlace googlePlace = this.googlePlaceRepository.findByGooglePlaceId(googlePlaceResult.placeId);
if (googlePlace != null) {
return googlePlace;
}
Place place = new Place();
place.setLatitude(googlePlaceResult.geometry.location.lat);
place.setLongitude(googlePlaceResult.geometry.location.lng);
place.setPlaceType(Place.PlaceType.GOOGLE_PLACE);
place.setName(googlePlaceResult.name);
place.setVicinity(googlePlaceResult.vicinity);
googlePlace = new GooglePlace();
googlePlace.setPlace(place);
googlePlace.setGooglePlaceId(googlePlaceResult.placeId);
return googlePlace;
}).collect(Collectors.toList());
this.googlePlaceRepository.saveAll(places);
// Add places to group..
}
}
Summary
I would like to know what I don't see. Am I fighting the framework, or does my data model not make sense and this is why I find myself struggling with this? Or am I still having issues on how the two patterns "Repository" and "DAO" are supposed to be used?
How would one implement this?
I would say you are correct that there are too many repository dependencies in your service. Personally, I try to keep the number of #Autowired dependencies to a minimum and I try to use a repository only in one service and expose its higher level functionality via that service. At our company we call that data sovereignty (in German: Datenhoheit) and its purpose is to ensure that there is only one place in the application where those entities are modified.
From what I understand from your code I would introduce a PlacesService which has all the Dependencies to the PlaceRepository, GooglePlaceRepository and GooglePlaces. If you feel like Service is not the right name you could also call it the PlacesDao, mark it with a Spring #Component annotation and inject all the Repositories, which are by definition collections of things
#Component
public class PlacesDao {
#Autowired
private PlaceRepository placeRepository;
#Autowired
private GooglePlaceRepository googlePlaceRepository;
This service/DAO could offer an API findPlacesForGroup(userGroup) and createNewPlace(...) and thus making your for Loop smaller and more elegant.
On a side note: you can merge your first four lines into just one. Java Optionals support a orElseThrow() method:
UserGroup userGroup = userGroupRepository.findById(groupId).orElseThrow(() ->
new EntityNotFoundException(String.format("User group with id %s not found.", groupId));
I think the foreach does not look like a good approach to me. You're doing way to much for just a single responsibility of a function. I would refactor this to a standart for loop.
Place place = new Place();
place.setLatitude(googlePlaceResult.geometry.location.lat);
place.setLongitude(googlePlaceResult.geometry.location.lng);
place.setPlaceType(Place.PlaceType.GOOGLE_PLACE);
place.setName(googlePlaceResult.name);
place.setVicinity(googlePlaceResult.vicinity);
place = this.placeRepository.save(place);
This part can easily be a method in a service.
UserGroupPlace.UserGroupPlaceId userGroupPlaceId = new
UserGroupPlace.UserGroupPlaceId();
userGroupPlaceId.setUserGroup(userGroup);
userGroupPlaceId.setPlace(place);
UserGroupPlace userGroupPlace = new UserGroupPlace();
userGroupPlace.setUserGroupPlaceId(userGroupPlaceId);
this.userGroupPlaceRepository.save(userGroupPlace);
That part as well.
googlePlace = new GooglePlace();
googlePlace.setPlace(place);
googlePlace.setGooglePlaceId(googlePlaceResult.placeId);
this.googlePlaceRepository.save(googlePlace);
And this part: I don't understand why your doing this. You could just update the googlePlace instance you loaded from the repo. Hibernate/Transactions are doing the rest for you.

resolveContextualObject and getConversationId in custom Spring scope

I am wondering what is the purpose of org.springframework.beans.factory.config.Scope.resolveContextualObject(String key) and org.springframework.beans.factory.config.Scope.getConversationId()?
From the javadoc:
Object resolveContextualObject(String key)
Resolve the contextual object for the given key, if any. E.g. the HttpServletRequest object for key "request".
String getConversationId()
Return the conversation ID for the current underlying scope, if any.
The exact meaning of the conversation ID depends on the underlying storage mechanism. In the case of session-scoped objects, the conversation ID would typically be equal to (or derived from) the session ID; in the case of a custom conversation that sits within the overall session, the specific ID for the current conversation would be appropriate.
This description doesn't tell me much.
Could you give me some examples which demonstrate how to make use of these methods?
My observation is that resolveContextualObject(String key) looks like a code smell, where where a Scope can expose some internal object.
Having:
public class MyCustomScope implements Scope {
private Pair<String, String> myPair;
#Override
public Object resolveContextualObject(String key) {
if ("myKey".equals(key)) return myPair;
return null;
}
// ...
}
#Configuration
public class RegisterMyScopeConfig {
#Bean
public BeanFactoryPostProcessor beanFactoryPostProcessor() {
return beanFactory -> beanFactory.registerScope(
"mycustomscope", new MyCustomScope());
}
}
Then you can:
#Scope("mycustomscope")
#Component
class MyComponent {
#Value("#{myKey.first}")
private String firstOfMyPair;
// or
#Value("#{myKey}")
private Pair<String,String> myPair;
}
Of course the way how you resolved object which matches key, might be fancier ;).
For example, in GenericScope it looks like that:
#Override
public Object resolveContextualObject(String key) {
Expression expression = parseExpression(key);
return expression.getValue(this.evaluationContext, this.beanFactory);
}

Jackson #JsonFilter is not getting applied when used at field or method level

I am using Spring version 4.3.3 and Jackson version 2.8.3. I am trying to filter out specific fields from an entity bean based on some custom logic that is determined at runtime. The #JsonFilter seems ideal for this type of functionality. The problem is that when I put it at the field or method level, my custom filter never gets invoked. If I put it at the class level, it gets invoked just fine. I don't want to use it at the class level though since then I would need to separately maintain the list of hardcoded field names that I want to apply the logic to. As of Jackson 2.3, the ability to put this annotation at the field level is supposed to exist.
Here is the most basic custom filter without any custom logic yet:
public class MyFilter extends SimpleBeanPropertyFilter {
#Override
protected boolean include(BeanPropertyWriter beanPropertyWriter) {
return true;
}
#Override
protected boolean include(PropertyWriter propertyWriter) {
return true;
}
}
Then I have the Jackson ObjectMapper configuration:
public class MyObjectMapper extends ObjectMapper {
public MyObjectMapper () {
SimpleFilterProvider filterProvider = new SimpleFilterProvider();
filterProvider.addFilter("myFilter", new MyFilter());
setFilterProvider(filterProvider);
}
}
Then finally I have my entity bean:
#Entity
public class Project implements Serializable {
private Long id;
private Long version;
#JsonFilter("myFilter") private String name;
#JsonFilter("myFilter") private String description;
// getters and setters
}
If I move the #JsonFilter annotation to the class level where #Entity is, the filter at least gets invoked, but when it is at the field level like in the example here, it never gets invoked.
I have the same need but after examining the unit tests I discovered that this is not the use-case covered by annotating a field.
Annotating a field invokes a filter on the value of the field not the instance containing the field. For example, imagine you have to classes, A and B, where A contains a field of type B.
class A {
#JsonFilter("myFilter") B foo;
}
Jackson applies "myFilter" to the fields in B not in A. Since your example contains fields of type String, which has no fields, Jackson never invokes your filter.
I have a need to exclude certain fields based on the caller's permissions. For example, an employee's profile may contain his taxpayer id, which is considered sensitive information and should only be serialized if the caller is a member of the Payrole department. Since I'm using Spring Security, I wish to integrate Jackson with the current security context.
public class EmployeeProfile {
private String givenName;
private String surname;
private String emailAddress;
#VisibleWhen("hasRole('PayroleSpecialist')")
private String taxpayerId;
}
The most obvious way to do this is to Jackson's filter mechanism but it has a few limitations:
Jackson does not support nested filters so adding an access filter prohibits using filters for any other purpose.
One cannot add Jackson annotations to existing, third-party classes.
Jackson filters are not designed to be generic. The intent is to write a custom filter for each class you wish to apply filtering. For example, I you need to filter classes A and B, then you have to write an AFilter and a BFilter.
For my use-case, the solution is to use a custom annotation introspector in conjunction with a chaining filter.
public class VisibilityAnnotationIntrospector extends JacksonAnnotationIntrospector {
private static final long serialVersionUID = 1L;
#Override
public Object findFilterId(Annotated a) {
Object result = super.findFilterId(a);
if (null != result) return result;
// By always returning a value, we cause Jackson to query the filter provider.
// A more sophisticated solution will introspect the annotated class and only
// return a value if the class contains annotated properties.
return a instanceof AnnotatedClass ? VisibilityFilterProvider.FILTER_ID : null;
}
}
This is basically a copy SimpleBeanProvider that replaces calls to include with calls to isVisible. I'll probably update this to use a Java 8 BiPredicate to make the solution more general but works for now.
This class also takes another filter as an argument and will delegate to it the final decision on whether to serialize the field if the field is visible.
public class AuthorizationFilter extends SimpleBeanPropertyFilter {
private final PropertyFilter antecedent;
public AuthorizationFilter() {
this(null);
}
public AuthorizationFilter(final PropertyFilter filter) {
this.antecedent = null != filter ? filter : serializeAll();
}
#Deprecated
#Override
public void serializeAsField(Object bean, JsonGenerator jgen, SerializerProvider provider, BeanPropertyWriter writer) throws Exception {
if (isVisible(bean, writer)) {
this.antecedent.serializeAsField(bean, jgen, provider, writer);
} else if (!jgen.canOmitFields()) { // since 2.3
writer.serializeAsOmittedField(bean, jgen, provider);
}
}
#Override
public void serializeAsField(Object pojo, JsonGenerator jgen, SerializerProvider provider, PropertyWriter writer) throws Exception {
if (isVisible(pojo, writer)) {
this.antecedent.serializeAsField(pojo, jgen, provider, writer);
} else if (!jgen.canOmitFields()) { // since 2.3
writer.serializeAsOmittedField(pojo, jgen, provider);
}
}
#Override
public void serializeAsElement(Object elementValue, JsonGenerator jgen, SerializerProvider provider, PropertyWriter writer) throws Exception {
if (isVisible(elementValue, writer)) {
this.antecedent.serializeAsElement(elementValue, jgen, provider, writer);
}
}
private static boolean isVisible(Object pojo, PropertyWriter writer) {
// Code to determine if the field should be serialized.
}
}
I then add a custom filter provider to each instance of ObjectMapper.
#SuppressWarnings("deprecation")
public class VisibilityFilterProvider extends SimpleFilterProvider {
private static final long serialVersionUID = 1L;
static final String FILTER_ID = "dummy-filter-id";
#Override
public BeanPropertyFilter findFilter(Object filterId) {
return super.findFilter(filterId);
}
#Override
public PropertyFilter findPropertyFilter(Object filterId, Object valueToFilter) {
if (FILTER_ID.equals(filterId)) {
// This implies that the class did not have an explict filter annotation.
return new AuthorizationFilter(null);
}
// The class has an explicit filter annotation so delegate to it.
final PropertyFilter antecedent = super.findPropertyFilter(filterId, valueToFilter);
return new VisibilityPropertyFilter(antecedent);
}
}
Finally, I have a Jackson module that automatically registers the custom annotaion introspector so I don't have to add it to each ObjectMapper instance manually.
public class FieldVisibilityModule extends SimpleModule {
private static final long serialVersionUID = 1L;
public FieldVisibilityModule() {
super(PackageVersion.VERSION);
}
#Override
public void setupModule(Module.SetupContext context) {
super.setupModule(context);
// Append after other introspectors (instead of before) since
// explicit annotations should have precedence
context.appendAnnotationIntrospector(new VisibilityAnnotationIntrospector());
}
}
There are more improvements that can be made and I still have more unit tests to write (e.g., handling arrays and collections) but this is the basic strategy I used.
You can try this approach for the same purpose:
#Entity
#Inheritance(
strategy = InheritanceType.SINGLE_TABLE
)
#DiscriminatorColumn(
discriminatorType = DiscriminatorType.STRING,
length = 2
)
#Table(
name = "project"
)
#JsonTypeInfo(
use = Id.CLASS,
include = As.PROPERTY,
property = "#class"
)
#JsonSubTypes({
#Type(
value = BasicProject.class,
name = "basicProject"
),
#Type(
value = AdvanceProject.class,
name = "advanceProject"
)})
public abstract class Project {
private Long id;
private Long version;
}
#Entity
#DiscriminatorValue("AD")
public class AdvanceProject extends Project {
private String name;
private String description;
}
#Entity
#DiscriminatorValue("BS")
public class BasicProject extends Project {
private String name;
}
I don't think you will make it work. I was trying and these are results of my investigation, maybe it will be helpful.
First of all, as #Faron noticed, the #JsonFilterannotation is applied for the class being annotated not a field.
Secondly, I see things this way. Let's imagine, somewhere in Jackson internals you are able to get the actual field. You can figure out if there is the annotation using Java Reflection API. You can even get the filter name. Then you get to the filter and pass the field value there. But it happens at runtime, how will you get the corresponding JsonSerializer of the field type if you decide to serialize the field? It is impossible because of type erasure.
The only alternative I see is to forget about dynamic logic. Then you can do the following things:
1) extend JacksonAnnotationIntrospector (almost the same as implement AnnotationIntrospector but no useless default code) overriding hasIgnoreMarker method. Take a look at this answer
2) criminal starts here. Kinda weird way taking into account your initial goal but still: extend BeanSerializerModifier and filter out fields there. An example can be found here. This way you can define serializer that actually doesn't serialize anything (again, I understand how strange it is but maybe one will find it helpful)
3) similar to the approach above: define useless serializer based on BeanDescription implementing ContextualSerializer's createContextual method. The example of this magic is here
Thanks to this really good blog, I was able to use #JsonView to filter out specific fields from an entity bean based on some custom logic that is determined at runtime.
Since the #JsonFilter does not apply for the fields within a class, I found this to be a cleaner workaround.
Here is the sample code:
#Data
#AllArgsConstructor
public class TestEntity {
private String a;
#JsonView(CustomViews.SecureAccess.class)
private Date b;
#JsonView(CustomViews.SecureAccess.class)
private Integer c;
private List<String> d;
}
public class CustomViews {
public static interface GeneralAccess {}
public static interface SecureAccess {}
public static class GeneralAccessClass implements GeneralAccess {}
public static class SecureAccessClass implements SecureAccess, GeneralAccess {}
public static Class getWriterView(final boolean hasSecureAccess) {
return hasSecureAccess
? SecureAccessClass.class
: GeneralAccessClass.class;
}
}
#Test
public void test() throws JsonProcessingException {
final boolean hasSecureAccess = false; // Custom logic resolved to a boolean value at runtime.
final TestEntity testEntity = new TestEntity("1", new Date(), 2, ImmutableList.of("3", "4", "5"));
final ObjectMapper objectMapper = new ObjectMapper().enable(MapperFeature.DEFAULT_VIEW_INCLUSION);
final String serializedValue = objectMapper
.writerWithView(CustomViews.getWriterView(hasSecureAccess))
.writeValueAsString(testEntity);
Assert.assertTrue(serializedValue.contains("a"));
Assert.assertFalse(serializedValue.contains("b"));
Assert.assertFalse(serializedValue.contains("c"));
Assert.assertTrue(serializedValue.contains("d"));
}

Preferable way between clean session and minimal database-access

consider the following scenario: i have a bean that handles user-searches with a lot of parameters used on many pages with different urls. many users may spent a larger time with custom-searches and currently i am hitting the database to load those static lists everytime.
#ManagedBean
#ViewScoped
public class SearchBean extends DefaultBean {
private String searchPath; //seo: build a url-friendly path depending on search-parameters
private List<Currency>currencies;
private List<Country>countries;
private List<Market>markets;
private List<DrugTypes>drugTypes;
private List<Products>products;
/**
* ...15 other lists
*/
private List<ResultData>results;
#PostConstruct
public void init(){
this.currencies = Currency.getAll(); //jpa-entities
this.countries = Country.getAll();
this.markets = Markets.getAll();
this.drugTypes = DrugTypes.getAll();
this.products = Products.getAll();
}
public String search(){
this.results = ResultData.getByParameters(getSearchParams());
//
//e.g. localhost:8080/myApp/search/markets/germany/class-alpha-products/rhesus?faces-redirect=true
return searchPath;
}
public List<Currency> getCurrencies() { return currencies; }
public void setCurrencies(List<Currency> currencies) { this.currencies = currencies; }
public List<Country> getCountries() { return countries; }
public void setCountries(List<Country> countries) { this.countries = countries; }
public void setMarkets(List<Market> markets) { this.markets = markets; }
public List<Market> getMarkets() { return markets; }
public void setDrugTypes(List<DrugTypes> drugTypes) { this.drugTypes = drugTypes; }
public List<DrugTypes> getDrugTypes() { return drugTypes; }
public List<Products> getProducts() { return products; }
public void setProducts(List<Products> products) { this.products = products; }
}
what is the recommend way regarding to the headline? my small gripe is, that i see 20 jpa-queries on the console although the list-data which is build with <h:selectOneMenu> on client-side does not change on new pages but must be included on every subpage.
leave it as it is
put all those lists as session-attributes and remove them in #predestroy when user leaves.
put the whole bean as sessionbean (i already have 2 session-beans ("user" and "language" and i read that having more is not a good design)
store the list-data as a json-string in a cookie and recreate the list if the cookie exists?
other suggestions?
thanks for watching!
None of all. Caching DB entities isn't the responsibility of a front end (UI) framework. That's the responsibility of the persistence (DB) framework, which is thus JPA in your case.
JPA offers 2nd level caching possibilities. Main advantage as compared to all your proposals is that it knows precisely which entities are more queried and thus need to be cached, and when exactly to invalidate a cached entity because of an entity change. JSF as being a dumb HTML form based MVC framework which only delegates user interface events/data to business services knows nothing of this all.
See also:
How to configure L2 cache in Hibernate/JPA2?
Hibernate 5.1 User Guide - Chapter 12: Caching
EclipseLink Wiki - Examples / JPA / Caching

Resources