I use camel-dozer for mapping MyObject to MyObjectDTO in my project. Spring repository return Page<MyObject>. I need to mapping Page<MyObject> to Page<MyObjectDTO>. Is exist nice way for this mapping without iteration Page.content and convert for each item?
I mapping single object this way:
dozerBeanMapping.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<mappings xmlns="http://dozermapper.github.io/schema/bean-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://dozermapper.github.io/schema/bean-mapping http://dozermapper.github.io/schema/bean-mapping.xsd">
<mapping>
<class-a>ru.test.MyObject</class-a>
<class-b>ru.test.MyObjectDTO</class-b>
<field>
<a>myField</a>
<b>testField</b>
</field>
</mapping>
</mappings>
RestRoute.java
rest("/search").description("")
.get("/{id}")
.consumes("application/json").to("direct:getMyObjectByID")
from("direct:getMyObjectByID")
.routeId("direct:getMyObjectByID")
.bean("myObjectRepository", "getMyObjectById(${header.id})")
.to("dozer:transformMyObjectDTO?mappingFile=dozerBeanMapping.xml&targetModel=ru.test.MyObjectDTO")
My temporary horrible solution: return immediately list of DTO from Spring Data repository.
#Repository("myObjectRepository")
public interface MyObjectRepository extends CrudRepository<MyObject, Integer> {
#Query(value = "SELECT new ru.test.MyObjectDTO(l.id, l.otherfields)" +
" from MyObject l where l.otherObject=:otherObject")
Page<MyObjectDTO> getAllByOtherObject(#Param("otherObject") OtherObject otherObject, Pageable pageable);
}
Related
i am using java, spring mvc and mybatis.
for http patch, it is used to update partial resource, while put updates the entirely resource.
my code looks like
#RestController
#RequestMapping("/test")
public class Test {
#PutMapping
public void update(MyBean myBean) {
//update MyBean
}
mybatis code is:
<update id="updateMyBean">
update My_Bean
<set>
<if test="filed1 != null>field1 = #{field1},</if>
<if test="filed2 != null>field1 = #{field2},</if>
<if test="filed3 != null>field1 = #{field3},</if>
</set>
where id = #{id}
</update>
then how to implement patch in the spring mvc? how to implement patch in mybatis?
is it add another update method like following?
#PutMapping
public void update(MyBean myBean) {
//update MyBean
}
#PatchMapping
public void updateBeanPartial(MyBean myBean) {
//update MyBean
}
//they look like the same just annotations and/or method name are different
or
#PatchMapping
public void updateBeanPartial(Map myBeanMap) {
//update MyBean
}
//use Map as parameters, but in this case, we cannot do bean validation easily and cannot show what fields need to be sent in swagger
//or use specified fields of MyBean as parameter, but it will introduce many controller methods because MyBean can have many fields
and they use same mybatis update statement?
Thus, how to implement put and patch in the code?
or their difference only in the semantic not in the code?
Lets clarify some things first:
Update: If Bean (MyBean) has multiple fields, then you create 1 SQL statement to update all fields once in your batis.
Patch: If Bean (MyBean) has multiple fields, then you create 1 SQL Statement to patch (updating only some fields).
In your Batis mapper XML, would you need to define 2 functions:
Update User ,
Patch User ,
What you define in your batis mapper is your DAO statements.
What you define your Spring MVC is your Controller/Services, and what you want to achieve.
If you want 2 endpoints (PUT, PATCH), and you want them to do different things, then you need 2 different statements in batis.
Take note of <mapper namespace="org.com.BatisUserService"> which is what you will use to later on reference the implementations from your other classes such as #Controller, or #Service in your Spring MVC app.
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="org.com.BatisUserService"> <---- Take note of this
<update id="updateUser"
parameterType="org.com.model.User">
UPDATE user SET
user_name=#{userName,jdbcType=VARCHAR},
user_password=#{userPassword,jdbcType=VARCHAR},
user_email=#{userEmail,jdbcType=VARCHAR}
WHERE
user_id=#{userId,jdbcType=INTEGER};
</update>
<update id="patchUser"
parameterType="org.com.model.User">
UPDATE user SET
user_name=#{userName,jdbcType=VARCHAR}
user_password=#{userPassword,jdbcType=VARCHAR},
WHERE
user_id=#{userId,jdbcType=INTEGER};
</update>
</mapper>
Then in your Controller, you would then call your Mapper org.com.BatisUserService which has the functions updateUser and patchUser defined:
#Controller
#RequestMapping(value = "/user")
public class UserController {
#Autowired
private BatisUserService batisUserService;
#RequestMapping(value = "/updateUser", method = RequestMethod.POST)
public User updateUser(User user) {
batisUserService.updateUser(user);
}
#RequestMapping(value = "/patchUser", method = RequestMethod.PATCH)
public User patchUser(User user) {
batisUserService.patchUser(user);
}
}
I have to modify (better say enhance) an already existing entity class with some metadata. The calss's metadata is defined via annotations. But now I'd like to add some UserType definitions (hibernate-specific) to the entity class. It works well if I repeat all the metadata set via annotation in my hibernate-mapping-file but is there a way to only add my specific aspect without overwriting the annotation based stuff? Thanks in advance.
Oh, sorry. Of course I can:
#Entity
public class Thing implements Serializable {
#Id
private ContractNumber contractNumber;
#Column
private String columnViaAnnotation;
public ContractNumber getContractNumber() {
return contractNumber;
}
public void setContractNumber(ContractNumber contractNumber) {
this.contractNumber = contractNumber;
}
public String getColumnViaAnnotation() {
return columnViaAnnotation;
}
public void setColumnViaAnnotation(String columnViaAnnotation) {
this.columnViaAnnotation = columnViaAnnotation;
}
}
My mapping:
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-mapping>
<class name="com.mypackage.Thing">
<id name="contractNumber" type="myType" column="contractNumber" />
</class>
<typedef class="com.mypackage.HibernateContractNumberConverter" name="myType" />
</hibernate-mapping>
The custom type works perfectly well. The datatype ContractNumber will be translated into a int and vice versa. But unfortunately the column 'columnViaAnnotation' disappears due to it's not mentioned in my mapping file. I was hoping that Hibernate is smart enough to merge both configurations (xml and annotation based). Is there any chance of doing this?
I have the following xml:
Company.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<company>
<employeeList>
<employee name="Jane Doe" id="A">
</employee>
<employee name="John Smith" id="B">
</employee>
<employee name="Anne Jones" id="C">
</employee>
</employeeList>
</company>
Department.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<departmentList>
<departmentList>
<department name="Dev" id="1">
<employee>A</employee>
<employee>B</employee>
</department>
<department name="Sales" id="2">
<employee>C</employee>
</department>
</departmentList>
</departmentList>
The class Department.java has an employee list
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Department {
#XmlAttribute
private String id;
#XmlElement(name="employee")
#XmlJavaTypeAdapter(EmpAdapter.class)
public List<Employee> employeeList;
public Department(){
employeeList = new ArrayList<Employee>();
}
and the employee.java class has an id attribute with the #XmlId tag.
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Employee {
#XmlAttribute
#XmlID
private String id;
I know how to get the employee objects to be pointed at from the department object using an adapter. However that is if the Company.xml file is unmarshalled first because it has the employee details in it.
So what I want is to be able to unmarshall the Department.xml first and have it create some sort of placeholder so that once the Company.xml has been unmarshalled the employeeList in the department object is filled in.
EDIT
POSSIBLE SOLUTION: As suggested by #laune I can use the adapter to create Employee objects as a placeholder, and then once the employee list is unmarshalled fill in these details in the employee objects under the department class
WHAT I NEED:
But this way I have to always handle each of the cases individually through code and new methods. Main problem is I cannot predict the order in which the unmarshalling will happen. Is there a way to have JAXB unmarshall without worrying about dependencies and ordering of creating objects? Maybe using schemas?
You can modify the EmpAdapter to return a placeholder Employee object created from an (additional) constructor
public Employee( String id ){
this.id = id;
}
Also, the EmpAdapter should maintain a Map<String,Employee> mapping ids to employees.
After you have unmarshalled the employees into a flat list, iterate the list and fill the fields of the placeholder (located via the map) from the list element.
Things would be much easier if employee data contains the deparment id.
I have different XML returns that I want to parse with JAXB with annotated classes. Many of the XML files share a similar top structure and with the contents of an inner tag that can vary. Since we sent the ContextInstance a class and not an instance I can't add classes.
How would you create a set of JAXB annotations that don't repeat the top level xml each time, given examples of two files like this.
<?xml version='1.0' encoding='UTF-8'?>
<eveapi version="2">
<currentTime>2008-11-24 20:14:29</currentTime>
<result>
<serverOpen>True</serverOpen>
<onlinePlayers>38102</onlinePlayers>
</result>
<cachedUntil>2008-11-24 20:17:29</cachedUntil>
</eveapi>
And another file like this:
<?xml version='1.0' encoding='UTF-8'?>
<eveapi version="2">
<currentTime>2012-02-12 14:39:12</currentTime>
<result>
<rowset name="characters" key="characterID" columns="name,characterID,corporationName,corporationID">
<row name="Alexis Prey" characterID="1365215823" corporationName="Puppies To the Rescue" corporationID="238510404"/>
</rowset>
</result>
<cachedUntil>2012-02-12 15:27:00</cachedUntil>
</eveapi>
A common top level class would be something like
#XmlRootElement(name="eveapi")
public class EveApiConfig {
#XmlElement(required = true)
protected Date currentTime;
#XmlElement(required = true)
protected String cachedUntil;
#XmlAttribute(name = "version", required = true)
protected int version;
#XmlElement(required = true)
protected ResultsConfig result;
}
But ResultsConfig would be number of other things. it's almost like a need a reverse XmlSeeAlso
Any thoughts if how to do this?
I think you can achieve this with several object factories. I'll sketch out the solution:
Make result an #XmlElementRef, typed with something like JAXBElement<AbstractResultConfig>
Per case create one package with an ObjectFactory
In which of the ObjectFactory classes implement a different #XmlElementDecl factory method which would create JAXBElements with instance of different subclasses of the AbstractResultConfig. Something like:
#XmlElementDecl(name = "Result")
public JAXBElement<ResultConfigImpl> createResultConfigImpl(ResultConfigImpl value) {
return new JAXBElement<ResultConfigImpl>(..., ResultConfigImpl.class, null, value);
}
I guess you'll need to pair ith with createResultConfig() method or something like this.
I am using Dozer to do object Mapping. Everything works reaaly well just that I'm not able to map this particular thing.
<mapping>
<class-a>User</class-a>
<class-b>UAUserBean</class-b>
<field>
<a>RightLst.Right</a>
<b>Rights</b>
<a-hint>Right</a-hint>
<b-hint>UARightBean</b-hint>
</field>
<field>
<a>RightLst.NumInLst</a>
<b>Rights.length</b>
</field>
</mapping>
//here RightLst is an object of a class and numInLst (int prop)
//rights is an array of objects
what i want to do is
lUser.getRightLst().setNumInLst(uaUserBean.getRights().length);
Any suggestions??
Thanks in advance.
User{
protected RightLst rightLst;
}
RightLst{
protected Integer numInLst;
protected Collection right = new ArrayList();
}
public class UAUserBean{
private UARightBean[] rights;
}
When you do this:
...
<b>rights.length</b>
</field>
Dozer will try to access the first position of the rights array and call the getter for the length property on an instance of UARightBean (which is the type of the array), obviously the length property doesn't exist in UARightBean and Dozer will throw an Exception.
I suggest to create a getter method in UAUserBean to return the length of the rights property, it would look like this:
class UAUserBean {
...
public int getRightsLength() {
return rights != null ? rights.length : 0;
}
}
Mapping file:
<class-a>User</class-a>
<class-b>UAUserBean</class-b>
<field>
<a>rightLst.numInLst</a>
<b>rightsLength</b>
</field>
If you can't modify UAUserBean, your last option would be a custom converter from UARightBean[] to Integer, but it would look ugly.