MyOM and MyEntity objects have an id as a field
The function map() works. If I set the id parameter, then my MyEntity in map() receives this parameter.
I want to do the same thing with mapDTOs(). When it receives a value for id it must be propagated to all calls of map() during the loop.
How can I do this?
#Mapping(target = "id", expression = "java(id)")
MyEntity map(MyOM om, String id);
#Mapping(target = ??, expression = ??)
List<MyEntity> mapDTOs(List<MyOM> dtos, String id);
The additional parameter should be annotated with #Context if meant to be passed to other mapping methods. The annotation was introduced in the 1.2 version, more info can be found in MapStruct documentation.
Your method should be declared without any #Mapping annotation as:
List<MyEntity> mapDTOs(List<MyOM> dtos, #Context String id);;
Since #Context parameters are not meant to be used as source parameters, a proxy method should be added to point MapStruct to the right single object mapping method to make it work.
default MyEntity mapContext(MyOM om, #Context String id) {
return map(om, id);
}
In case there is no proxy method, MapStruct would generate a new method for mapping a single object without mapping id because the existing one doesn't contain a second parameter marked as #Context.
This solution is simple but perhaps doesn't use #Context in the right way, for a better understanding of #Context usage check Filip's answer.
Related
I'm working on a project in which I need to pass parameters to the #subselect annotation of spring boot (which maps a request to an entity), like the following example:
#Entity
#Immutable
#Subselect("SELECT FROM Employe INNER JOIN Employe_adress ON Employe.id = Employe_adress.eid WHERE Employe_adress.aid=?x")
public class Employe {
...
}
I want to bind an external value to "x" variable. Thank you.
Edit: One method I find is adding a global variable, but "The value for annotation attribute must be a constant expression".
It can be done with #FilterDef and #Filter: https://www.baeldung.com/hibernate-dynamic-mapping#parameterized-filtering-with-filter
Defining the #Filter
To demonstrate how #Filter works, let's first add the following filter definition to the Employee entity:
#FilterDef(
name = "incomeLevelFilter",
parameters = #ParamDef(name = "incomeLimit", type = "int")
)
#Filter(
name = "incomeLevelFilter",
condition = "grossIncome > :incomeLimit"
)
public class Employee implements Serializable {
The #FilterDef annotation defines the filter name and a set of its parameters that will participate in the query. The type of the parameter is the name of one of the Hibernate types (Type, UserType or CompositeUserType), in our case, an int.
The #FilterDef annotation may be placed either on the type or on package level. Note that it does not specify the filter condition itself (although we could specify the defaultCondition parameter).
This means that we can define the filter (its name and set of parameters) in one place and then define the conditions for the filter in multiple other places differently.
This can be done with the #Filter annotation. In our case, we put it in the same class for simplicity. The syntax of the condition is a raw SQL with parameter names preceded by colons.
Here s my CODE to start with:
PersonController.java
#RequestMapping(value = "/person", method = RequestMethod.POST)
public ResponseEntity<?> addPerson(#Valid Person p, HttpServletResponse response) {
...
}
Person.java
public class Person {
#NotNull
String name;
#NotNull
int age;
String gender;
}
The requirement is: When a POST request is made to /person, I want an exception to be thrown if the user did not specify a key for the string Name in the BODY of the request. The annotation #NotNull does not do this.
Is there another annotation that I can use in Person.java to achieve this? If not, is there some validation I could do in the addPerson method to ensure that an exception is thrown if one of the mandatory parameters are not there?
Actually the #NotNull annotation does exactly what you want but unfortunately it can't do it on int type since it can't be null. In order for it to work you need to change the age to Integer and then after the spring does the binding of values if both parameters are passed and they have values the validation will pass. Otherwise if they are passed with empty value or not passed at all the value will be null and the validation will fail. Just make sure that you don't have some constructor for Person that initializes the attributes to some values.
If you don't want to change it and use an int you can add HttpServletRequest request to the method arguments and check if there is a parameter age present with:
request.getParameter('age');
If it is null then no parameter was passed at all.
Hint: It may be that you are missing some configuration and the annotation are not processed at all, something like <mvc:annotation-driven/> or #EnableWebMvc or maybe you are missing an actual validator implementation like Hibernate Validator. It is hard to tell without a sample of your configuration.
First, you need to encapsulate the fields in your domain-classes. The spring container will use these getters and setters to manipulate the object.
Then, you can add constraints to these getters and setters (Bean Validation). If you added them correctly, Spring will catch errors when using the #Valid annotation (which you did). These errors will be added to the BindingResult, and can be shown in a jsp by using the Spring form tags.
<form:errors path="field_that_was_manipulated_incorrectly" />
#RestController
#RequestMapping(value = "/players")
public class REST {
#RequestMapping(method = RequestMethod.GET)
public List<Player> getAll() {
return service.getAll();
}
#RequestMapping(method = RequestMethod.GET, params = {"team", "score"})
public List<Player> getByPlayerAndScore(#RequestParam(value = "team") String team,
#RequestParam(value = "score", required = false) int score) {
return service.getByPlayerAndScore(team, score);
}
}
Q1: I am expecting first method to work for url "/players" (worked as expected) and second method to work for url's ("/players?team=xyz", "/players?team=xyz&score=1000"). spring used method1 for "/players?team=xyz". Even i specified score as optional, unless i specify 2 params, spring is is not using 2nd method. How to solve this and what is best way of writing controller methods to handle these type of requests where user can send different sets of available params (like param1¶m2, only param1, only param2 etc).
Q2: For the 2nd type of query with different sets of params, how to write database queries in DAO layer. should i write separate methods each with a different query or one method with multiple if statements(like if user sent 'team' add team to DB query, if user sent 'score' add it to DB query ...)
Your second mapping explicitly specifies that both the team and score request parameters must be present, and they aren't in your example of /players?team=xyz. The fact that you wrote required = false on the binding for the method parameter is irrelevant--the #RequestMapping explicitly says that the parameter must be present in the request. (Note that you can have a parameter but no value, as in the common Spring Security URL of /login?error. This would match params = { "error" } but would have no value.)
It might be simpler to use a regular if statement in a single controller method.
Alternately, you may want to look into Spring's Querydsl integration, which allows you to interpret query parameters as a Predicate that can be passed directly to a Spring Data finder method.
Also, look into the new composite convenience annotations such as #GetMapping, which will make your code a bit simpler to read.
I want to write a finder method in my repository to find an object based on one field OR another one while supplying one parameter like:
#RepositoryDefinition(domainClass = Person.class, idClass = Long.class)
public interface PersonRepository extends CrudRepository<Person, Long> {
List<Person> findAllByIdOrAnotherId(someId);
}
How can I do that without using SQL?
I added a second parameter to the method and it worked.
List<Transaction> findAllByIdOrParentId(Long id, Long parentId);
This is just a definition for the method because I pass the same parameter to the method from the service as:
List<Transaction> transactions = transactionRepository.findAllByIdOrParentId(transactionId, transactionId);
One of the cool things about Spring Data JPA is the fact you can define custom queries as abstract methods in your interface.
In a nutshell, you can define a new search criteria just by specifying the #Entity field name you want to query by. So, let's say i want to findAll() by Id and CustomId. Both Id and CustomId are fields on my domain class. I would do:
List<Person> findAllByIdOrCustomId(someId, someCustomId);
For more info, research the link below:
Spring Data JPA - DEFINE QUERY METHODS
I have
#Document
public class Employee
{
#Id
Long empCode;
String empSurname;
String address;
// getters setters
}
I have written a query using Mongo Repositories like
public interface EmployeeRepository extends MongoRepository<Employee, Long>
{
List<Employee> findEmployeesByEmpCode(int empCode);
#Query(value="{ 'empCode' : ?0 }", fields="{ 'address' : 1}")
List<String> findAddressByEmpCode(int empCode);
The first query works but the second fails. It works only if I change the returned type of List to Employee.
The error at failure is
java.lang.IllegalArgumentException: [Assertion failed] - this argument is required; it must not be null
at org.springframework.util.Assert.notNull(Assert.java:115)
at org.springframework.util.Assert.notNull(Assert.java:126)
at org.springframework.data.convert.EntityInstantiators.getInstantiatorFor(EntityInstantiators.java:86)
at org.springframework.data.mongodb.repository.query.DtoInstantiatingConverter.<init>(DtoInstantiatingConverter.java:61)
at org.springframework.data.mongodb.repository.query.MongoQueryExecution$ResultProcessingConverter.convert(MongoQueryExecution.java:376)
at org.springframework.data.mongodb.repository.query.MongoQueryExecution$ResultProcessingExecution.execute(MongoQueryExecution.java:345)
at org.springframework.data.mongodb.repository.query.AbstractMongoQuery.execute(AbstractMongoQuery.java:91)
Obviously, it cannot work out how to convert the values from the contained Employee object to String.
How can I implement this ? Any example would be much appreciated.
Thanks,
The fields attribute is used to filter properties which are not set into your domain objects (Employee). It has not much to do with MongoDB projection mechanism.
In order to use projection here, see projections in the reference documentation.
You will have to create a projection interface:
interface AddressesProjection { String getAddress(); }
and change the repository method signature accordingly:
List<AddressesProjection> findByEmpCode(int empCode);
Projections declare a contract between the underlying type and the method signatures related to the exposed properties. Hence it is required to name getter methods according to the property name of the underlying type.
This is the closest you can get, afaik.