Default value with #ParameterObject with Spring - spring

We have an endpoint with a lot of boolean on it, with default values. I wanted to encapsulate those into an object thanks to the #ParameterObject annotation.
However, I haven't found a way to set default values for it. My idea was to have something similar to what #PageableDault does.
Here the endpoint:
public List<Person> getPersons(#ParameterObject SearchCriteria profileSearchCriteria) {
// beautiful code
}
And here the object (it uses Lombok but shouldn't have an impact I would say :)):
#Builder
#Data
#Jacksonized
public class ProfileSearchCriteria {
#Builder.Default
private boolean criteria1 = false;
#Builder.Default
private boolean criteria2 = false;
#Builder.Default
private boolean criteria3 = false;
Do you know how can I setup default values for the #ParameterObject annotation?

Related

Bidirectional #OneToOne Spring Data JPA, Hibernate

I am using Bidirectional #OneToOne from Hibernate documentation. I have created an identical model for the test.
I can't get Phone via PhoneDetails. I get an error - Message Request processing failed; nested exception is org.hibernate.LazyInitializationException: could not initialize proxy [com.example.model.Phone#1] - no Session.
I've tried many options and it doesn't work.
Please tell me how to get the Phone correctly? I sit all day trying to do this. I did not find any options on the Internet, so I ask here.
Phone.java
#Entity(name = "Phone")
public class Phone {
#Id
#GeneratedValue
private Long id;
#Column(name = "`number`")
private String number;
#OneToOne(mappedBy = "phone",
cascade = CascadeType.ALL,
orphanRemoval = true,
fetch = FetchType.LAZY)
private PhoneDetails details;
public Phone() {
}
public Phone(String number) {
this.number = number;
}
// Getters and setters are omitted for brevity
public void addDetails(PhoneDetails details) {
details.setPhone( this );
this.details = details;
}
public void removeDetails() {
if ( details != null ) {
details.setPhone( null );
this.details = null;
}
}
}
PhoneDetails.java
#Entity(name = "PhoneDetails")
public class PhoneDetails {
#Id
#GeneratedValue
private Long id;
private String provider;
private String technology;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "phone_id")
private Phone phone;
public PhoneDetails() {
}
public PhoneDetails(String provider, String technology) {
this.provider = provider;
this.technology = technology;
}
// Getters and setters are omitted for brevity
}
LifecycleController.java
#Controller
public class LifecycleController {
#Autowired
ServiceJpa serviceJpa;
#GetMapping(value = "/savePhoneAndPhoneDetails")
public String savePersonAddress () {
Phone phone = new Phone( "123-456-7890" );
PhoneDetails details = new PhoneDetails( "T-Mobile", "GSM" );
phone.addDetails( details );
serviceJpa.savPhone( phone );
return "/savePhoneAndPhoneDetails";
}
#GetMapping(value = "/getPhone")
public String addPersonAddress () {
PhoneDetails address = serviceJpa.findPhoneDetailsById(2L).orElseThrow();
Phone phone = address.getPhone();
/*
An error appears here -
could not initialize proxy
[com.example.model.Phone#1] - no Session
*/
System.out.println(phone.getNumber());
return "/getPhone";
}
}
ServiceJpa.java
#Service
#Transactional
public class ServiceJpa {
#Autowired
PhoneJpa phoneJpa;
#Autowired
PhoneDetailsJpa phoneDetailsJpa;
#Transactional
public void savPhone(Phone phone) {
phoneJpa.save(phone);
}
#Transactional
public Optional<PhoneDetails> findPhoneDetailsById(Long id) {
return phoneDetailsJpa.findById(id);
}
}
interface PhoneJpa.java
#Repository
public interface PhoneJpa extends JpaRepository<Phone, Long> {
}
interface PhoneDetailsJpa.java
#Repository
public interface PhoneDetailsJpa extends JpaRepository<PhoneDetails, Long> {
}
I agree with Andriy's comment with a slight addition of "You should not access [lazily loaded] entity details outside transaction bounds". But, for starters, is there some reason you want the OneToOne to be FetchType.LAZY to begin with? If you changed it to EAGER, your "lazy" problem would be resolved by virtue of it no longer being a lazy reference but being a real hydrated object.
If that is not the exact route you want to take, there are a dozen ways to EAGERLY fetch things in general and frankly too many to present a single solution here as best/ideal. As your code exists, since all the dereferencing (for now) is happening inside your Controller, then Andriy's suggestion to add #Transaction to the Controller may suffice in that it will be lazily fetched when you need it.
But in the future, if you have Lazy elements in a POJO that get returned to the stack higher than the controller, say, just before they are serialized to JSON for example, then even the CONTROLLER's #Transactional wouldn't be "high" enough in the stack and you'll end up with the same Lazy init problem..
Also, by having it be Lazy and then dereferencing it elsewhere, you're guaranteeing two trips to the Database. With proper FETCH/JOIN eager loads, you would limit that to one, which can be another performance benefit.
So either way, you're back to the real problem at hand.. looking for ways to ensure your operations occur ENTIRELY inside a Transaction boundary OR having to completely hydrate the object so no "Lazy" danglers get dereferenced outside of that.. i.e. by making them eager or by force-initializing any potential Lazy proxies/collections.

Filter results from QueryDSL search

I'm using QueryDSL as part of Spring Data Rest to search entities from our API.
Is it possible to somehow filter the search API, so that by default it won't find for example Car entities that are "deactivated"?
Currently I've a flag on car entity that when it's set to true, it shouldn't be exposed through our search API, and the cars that have this property set should be left out from search.
https://docs.spring.io/spring-data/jpa/docs/1.9.0.RELEASE/reference/html/#core.web.type-safe
In case of using Spring Data REST and QueryDSL, to change standard behavior of queries we can use aspects.
For example: we need to show by default only those Models whose flag is set to true:
#Data
#NoArgsConstructor
#Entity
public class Model {
#Id #GeneratedValue private Integer id;
#NotBlank private String name;
private boolean flag;
}
In this case we implement the aspect like this:
#Aspect
#Component
public class ModelRepoAspect {
#Pointcut("execution(* com.example.ModelRepo.findAll(com.querydsl.core.types.Predicate, org.springframework.data.domain.Pageable))")
public void modelFindAllWithPredicateAndPageable() {
}
#Around("modelFindAllWithPredicateAndPageable()")
public Object filterModelsByFlag(final ProceedingJoinPoint pjp) throws Throwable {
Object[] args = pjp.getArgs();
Predicate predicate = (Predicate) args[0];
BooleanExpression flagIsTrue = QModel.model.flag.eq(true);
if (predicate == null) {
args[0] = flagIsTrue;
} else {
if (!predicate.toString().contains("model.flag")) {
args[0] = flagIsTrue.and(predicate);
}
}
return pjp.proceed(args);
}
}
This aspect intercepts all calls of method findAll(Predicate predicate, Pageable pageable) of our repo, and add the filter model.flag = true to the query if the request parameters was not set (predicate == null), or if they don't contain 'flag' parameter. Otherwise aspect does not modify original predicate.

Make #ConfigurationProperties properties partially mandatory

Given the following simple (not nested) configuration properties class:
#ConfigurationProperties("env")
public class MyServiceProperties {
private String anyProperty;
private Boolean anyOther;
...
}
How can I make sure that anyProperty is mandatory, i.e. env.any-property must be set to startup the application? Is there any difference for nested configuration property classes?
You can perform all kind of validations.
#Validated
#ConfigurationProperties("env")
public class MyServiceProperties {
#NotNull
#Min(5)
private String anyProperty;
// this is for nested objects
#Valid
#NotNull
private FooNested fooNested;
public static class FooNested{
#NotNull
private String someVal;
}
}
You could also perform manual validation in setter
#Validated
#ConfigurationProperties("env")
public class MyServiceProperties {
private String anyProperty;
public void setAnyProperty(String anyProp){
// just an example
if(anyProp.lenght < 6){
throw new RuntimeException();
}
this.anyProperty = anyProp;
}
}

How to use Java 8 Optional with Moxy and Jersey

Is it possible to use Jersey with Moxy to/from Json and Java 8 Optionals?
How to configure it?
You can declare following class:
public class OptionalAdapter<T> extends XmlAdapter<T, Optional<T>> {
#Override
public Optional<T> unmarshal(T value) throws Exception {
return Optional.ofNullable(value);
}
#Override
public T marshal(Optional<T> value) throws Exception {
return value.orElse(null);
}
}
And use like this:
#XmlRootElement
public class SampleRequest {
#XmlElement(type = Integer.class)
#XmlJavaTypeAdapter(value = OptionalAdapter.class)
private Optional<Integer> id;
#XmlElement(type = String.class)
#XmlJavaTypeAdapter(value = OptionalAdapter.class)
private Optional<String> text;
/* ... */
}
Or declare in package-info.java and remove #XmlJavaTypeAdapter from POJOs:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlJavaTypeAdapters({
#XmlJavaTypeAdapter(type = Optional.class, value = OptionalAdapter.class)
})
But here are some drawbacks:
Adapter above can only work with simple types like Integer, String, etc. that can be parsed by MOXY by default.
You have to specify #XmlElement(type = Integer.class) explicitly to tell the parser type are working with, otherwise null values would be passed to adapter's unmarshal method.
You miss the opportunity of using adapters for custom types, e.g. custom adapter for java.util.Date class based on some date format string. To overcome this you'll need to create adapter something like class OptionalDateAdapter<String> extends XmlAdapter<String, Optional<Date>>.
Also using Optional on field is not recommended, see this discussion for details.
Taking into account all the above, I would suggest just using Optional as return type for your POJOs:
#XmlRootElement
public class SampleRequest {
#XmlElement
private Integer id;
public Optional<Integer> getId() {
return Optional.ofNullable(id);
}
public void setId(Integer id) {
this.id = id;
}
}

LazyInitializationException in AOP logger aspect

I have an app build with Spring, JSF JPA and Hibernate.
I have an aspect that "watches" every update*, create*,delete* method from my service layer of the application. This aspect logs the method params, apply toString to every param and them log them in the database. The problem is that I use domain objects in jsf and when i try to update* something i get a LazyInitializationException when toString() is applied to the method param.
One solution would be to remove from toString all params that represents other objects but then the operation has no sense as i do not log the details that interests me.
ie. I have an entity called Price which has a dependency PriceList:
public class Price extends BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "price")
private Double price;
//bi-directional many-to-one association to TelCoPriceList
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "price_list_id")
private PriceList priceList;
.....................
}
And the price is used into AddPriceForm.xhtml, which is the jsf form. The JSF AddPriceMB has a reference to PriceService which performs the actual update.
The PriceService is "monitored" by this aspect:
#Aspect
#Named("auditLogAspectImpl")
public class AuditLogAspectImpl implements AuditLogAspect {
#Inject
private UserService userService;
#Inject
private AuditLogService auditLogService;
#Override
#AfterReturning(pointcut = "execution(* com.videanuadrian.core.impl.services..*.save*(..)) or execution(* com.videanuadrian.core.impl.services..*.update*(..)) or execution(* com.videanuadrian.core.impl.services..*.delete*(..))", returning = "retVal")
public boolean afterLogEvent(JoinPoint joinPoint,Object retVal) {
String className = joinPoint.getTarget().getClass().getName();
String methondName = joinPoint.getSignature().getName();
...................................................................
StringBuffer logMessage = new StringBuffer();
Object[] args = joinPoint.getArgs();
//for login action we only get the username and hash the password
if (methondName.compareTo("login") == 0){
logMessage.append(args[0]);
}else {
int argsLen = args.length;
//else log all the parameters
for (int i = 0; i < argsLen; i++) {
// this is where the exception occurs !!!!!!!!!!!!!!!!!!!!!!!!!!!
logMessage.append(args[i]).append(",");
}
if (argsLen > 0) {
logMessage.deleteCharAt(logMessage.length() - 1);
}
}
//some save/update methods return Boolean
Boolean status = false;
if (retVal instanceof Boolean){
status = (Boolean) retVal;
}
//some return the ID of the object inserted,and if these methods return an integer the status is true, if they return null, the status si false
if (retVal instanceof Integer){
if (retVal!=null)
status = true;
}
auditLogService.addAuditLogEvent(uid, className+"."+methondName, status,logMessage.toString());
return true;
}
On a prior version I did not had this problem because I had used DTO objects and the conversion between DTO and Domain Objects was performed in the service layer. The error is very clear, as my session was closed long ago compared to the time that I perform this toString() operation.
Any idea how can i achieve this without using openSessionInView or extended persistence context? Or maybe my approach is not good....

Resources