I feel like this should be pretty straightforward, but I'm not sure about the actual code for it. Basically, I have my rest controller taking in 6 arguments, passing that through the Service and then using those arguments to build the object inside of the ServiceImplementation. From there I return a call to my repo using the object I just made. This call should attempt to query the database specific parameters of the object.
This query is the part where I'm not sure how to write using Spring JPA standards. I'd like to just use the variables I set my object with, but I'm not sure if I'll have to write out a query or if spring JPA can make it a bit more simple?
Code:
Controller:
#RestController
public class exampleController {
#Autowired
private ExampleService exampleService;
#GetMapping("/rest/example/search")
public exampleObj searchExample (#RequestParam(value = "exLetter") String exLetter,
#RequestParam(value = "exLang") String exLang, #RequestParam(value = "exType")int exType,
#RequestParam(value = "exMethod") String exMethod, #RequestParam(value = "exCd") String exCd,
#RequestParam(value = "exOrg") String exOrg) {
return exampleService.getExampleLetter(exLetter, exLang, exType, exMethod, exCd, exOrg);
}
}
ExampleSerivce:
public interface ExampleService {
public ExampleLetter getExampleLetter(String exLetter, String exLang, int exType, String exMethod, String exCd, String exOrg);
}
ExampleServiceImplementation:
#Service
public class ExampleServiceImpl implements ExampleService {
#Autowired
private ExampleRepository exampleRepo;
#Override
public ExampleLetter getExampleLetter(String exLetter, String exLang, int exType, String exMethod, String exCd, String exOrg) {
ExampleLetter examp = new ExampleLetter();
examp.setExCd(exCd);
examp.getKey().setExampleNumber(exLetter);
examp.getKey().setLanguageType(exLang);
examp.getKey().setMethod(exMethod);
examp.getKey().setMarketOrg(exOrg);
examp.getKey().setType(exType);
return exampleRepo.findExampleLetter(examp);
}
}
Repo:
#Repository
public interface ExampleRepository extends CrudRepository<ExampleLetter, ExampleLetterKey> {
}
If I understand it correctly, you are trying to make a dinamic query, based on filtering values that may or may not be there. If that's the case, you can use the Specification class to create the query dinamically:
First, in your Repository class, extend JpaSpecificationExecutor<ExampleLetter>:
#Repository
public interface ExampleRepository extends CrudRepository<ExampleLetter, ExampleLetterKey>, JpaSpecificationExecutor<ExampleLetter> {
}
Now, you will need a method (I'd sugest you put it in an specific class, for organization sake) to generate the query itself:
public class GenerateQueryForExampleLetter {
ExampleLetter exampleLetter;
public Specification<ExampleLetter> generateQuery() {
return new Specification<ExampleLetter>() {
private static final long serialVersionUID = 1L;
#Override
public Predicate toPredicate(Root<ExampleLetter> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
Predicate pred = null;
List<Predicate> predicates = new ArrayList<Predicate>();
if (this.exampleLetter.getExCd()!= null && !this.exampleLetter.getExCd().isEmpty()) {
predicates.add(builder.equal(root.<String>get("exCd"), this.exampleLetter.getExCd()));
}
...................
if (this.exampleLetter.getTheFieldYouNeed()!= null && !getTheFieldYouNeed.isEmpty()) {
predicates.add(builder.equal(root.<TheTypeOfTheField>get("theFieldYouNeed"), this.exampleLetter.getTheFieldYouNeed()));
}
if (!predicates.isEmpty()) {
pred = builder.and(predicates.toArray(new Predicate[] {}));
}
return pred;
}
};
}
public void setExampleLetter (ExampleLetter el) {
this.exampleLetter = el;
}
}
Finally, in your service class:
#Override
public ExampleLetter getExampleLetter(String exLetter, String exLang, int exType, String exMethod, String exCd, String exOrg) {
ExampleLetter examp = new ExampleLetter();
examp.setExCd(exCd);
examp.getKey().setExampleNumber(exLetter);
examp.getKey().setLanguageType(exLang);
examp.getKey().setMethod(exMethod);
examp.getKey().setMarketOrg(exOrg);
examp.getKey().setType(exType);
GenerateQueryForExampleLetter queryGenerator = new GenerateQueryForExampleLetter ();
queryGenerator.setExampleLetter(examp);
return exampleRepo.findAll(queryGenerator.generateQuery());
}
Note that the JpaSpecificationExecutor interface adds a few utility methods for you to use which, besides filtering, supports sorting and pagination.
For more details, check here, here, or this answer.
I have a POJO and I am using Jackson to deserialize it into the POJO.I want to log key value attributes which I'm not aware of. For this I use JsonAnySetter in the following manner:
#Value
#Slf4j
#Builder
public class Book {
private String titleId;
private String bookName;
private List<String> authors;
#JsonAnySetter
public void ignored(String key, Object value) {
log.warn("Received a key which hasn't been mapped. Key: {}, Value: {}", key, value);
}
}
Now to test this in my Junit how do I verify that the method ignored is called?
#Test
public void given_unknown_key_ensure_it_is_logged() {
ObjectMapper objectMapper = new ObjectMapper();
String test = "{\"randomField\": \"test\"}";
Book book = objectMapper.readValue(test, Book.class);
ArgumentCaptor<Book> captor = ArgumentCaptor.forClass(Book.class);
verify(book, times(1)).ignored("randomField", "test");
}
I need to use the actual Book object, but that has to be mocked as well, so not sure how I should proceed in this case.
Create a mock object of the Log object and set it in the Book class.
This assumes that the Log object is static,
which seems reasonable.
I'm using Spring with Gson to object serialization.
I have model objects that use #Expose annotation e.g.:
public class Zone {
#Expose
private String name;
#Expose
private String description;
#Expose
private List<String> longList;
private String someIrrelevantVar;
}
I'm have 2 controllers which serves Zone objects list to user e.g.:
#RestController
class ZoneController {
#GetMapping(value = "fullData")
List<Zone> getFullZones() {
return zoneService.getZones();
}
}
#RestController
class SimpleZoneController {
#GetMapping(value = "simpleData")
List<Zone> getSimpleZones() {
return zoneService.getZones();
}
}
The problem is List<String> longList var - it usually has a lot of entries (String is only example, in code it could be complex object).
In my getFullZones() I want to serve to user zones with this longList but in getSimpleZones() I want ot serve zones without longList (it's not used in any way on the client side).
How to do that?
I could iterate list with zones and set longList to null but it's not very elegant solution.
I'm setting up Spring to use Gson like this:
#Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(createGsonHttpMessageConverter());
super.configureMessageConverters(converters);
}
private GsonHttpMessageConverter createGsonHttpMessageConverter() {
Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
//.registerTypeAdapter - register some deserializers
.create();
GsonHttpMessageConverter gsonConverter = new GsonHttpMessageConverter();
gsonConverter.setGson(gson);
return gsonConverter;
}
}
Create a base class ZoneSimple and extend another class Zone extends ZoneSimple. Move the #Expose from fields to methods.
In the base class the method has no annotation. In the Zone the method is annotated.
Alternatively you can add a ProxyZone class which keeps zone instance and delegates all the calls to the instance. Annotate the Proxy as you need.
Another way is
Gson gson = new GsonBuilder()
.addSerializationExclusionStrategy(new ExclusionStrategy() {
#Override
public boolean shouldSkipField(FieldAttributes f) {
return f.getName().toLowerCase().contains("fieldName");
}
#Override
public boolean shouldSkipClass(Class<?> aClass) {
return false;
}
})
.create();
Got from the answer
Given the following jackson annotated class :
public class AClass {
#JsonProperty("propertyName")
private String anyProperty
public String getAnyProperty() {
...
}
...
}
or a mixin configuration:
public class AClass {
private String anyProperty
public String getAnyProperty() {
...
}
...
}
public interface AClassMixin {
#JsonProperty(value = "propertyName")
String getAnyProperty();
}
How can I get the json property "propertyName' reader method using jackson?
I need something like that:
ObjectMapper mapper = new ObjectMapper();
Method method = mapper.getReaderMethodForProperty("propertyName", Aclass.class);
Construct a JavaType for your bean class
JavaType target = objectMapper.constructType(AClass.class);
then use the ObjectMapper's DeserializationConfig to introspect it. This will give you a BeanDescription.
BeanDescription beanDescription = objectMapper.getDeserializationConfig().introspect(target)
You can use that to get a list of its BeanPropertyDefinition instances.
List<BeanPropertyDefinition> beanPropertyDefinitions = beanDescription.findProperties();
Each BeanPropertyDefinition has methods to retrieve getters and setters (and other things) as AnnotatedMember values from which you can retrieve the Member (you'll need to cast to Method).
for (BeanPropertyDefinition bpd : beanPropertyDefinitions) {
AnnotatedMember annotatedMember = bpd.getAccessor();
Member member = annotatedMember.getMember();
if (member instanceof Method) {
Method getterMethod = (Method) member;
System.out.println(getterMethod.getName());
}
}
I've seen many questions around using jackson to serialize/deserialize java objects using builder patter, however, I can't figure out why this code below won't work. I'm using Jackson version 2.5.4
#JsonDeserialize(builder = User.Builder.class)
public class User {
private String name;
private User(Builder builder) {
this.name=builder.name;
}
#JsonPOJOBuilder(buildMethodName = "build")
public static class Builder {
private String name;
public Builder name(String name) {
this.name = name;
return this;
}
public User build() {
return new Learner(this);
}
}
}
Trying to output the string representation always prints an empty list {}
By default the #JsonPOJOBuilderexpects the builder methods to starts with with prefix.
You should override this in the annotation: #JsonPOJOBuilder(withPrefix = "")
You should also mark the name field with the #JsonProperty annotation, or add a getter, or use the JacksonFeatureAutoDetect feature; otherwise Jackson does not see name as a JSON property.