About mybatis resultmap incremental mapping behavior change - spring-boot

I am handling mybatis version upgrade. When I upgrade from 3.3.1 to 3.5.1, I found that the automatic mapping behavior of resultMap has changed
here is an example
#Data
public class RequestLog {
private String apiVersion;
private String url;
}
public interface RequestLogMapper {
List<RequestLog> getAll();
}
resultMap
<?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="test.mapper.RequestLogMapper">
<resultMap type="test.entity.RequestLog" id="RequestLogMap">
<result property="apiVersion" column="api_version" jdbcType="VARCHAR"/>
<result property="url" column="url" jdbcType="VARCHAR"/>
</resultMap>
<select id="get" resultMap="RequestLogMap">
select api_version as apiVersion,
url
from request_log
limit 10;
</select>
</mapper>
In this example, an incorrect mapping of apiVersion is defined, but it can be assigned correctly in version 3.3.1, and the result is as follows
RequestLog(apiVersion=V3, url=/buy)
When upgrading to 3.5.1, apiVersion cannot be assigned correctly, and the result is as follows
RequestLog(apiVersion=null, url=/buy)
I read the official documentation and understand that resultMap supports incremental automatic mapping by default, so the wrongly written apiVersion field in version 3.3.1 will be ignore and not affect the automatic mapping rules, but why is the automatic mapping not work in 3.5.1?

Related

How to use MyBatis and Camel and Spring with annotations (interface Mapper)

How to use MyBatis with Camel flow with Spring(Boot) WITH annotations (intefrace)?
It is working fine when I use Mybatis configuration in XML file.
Like:
<to uri="mybatis:selectSomething...">
(old fashion of springboot|camel)
with:
<mapper namespace="Something">
<resultMap id="SomeObject" type="SomeObjectImpl">
...
</resultMap>
<select id="selectSomething" resultMap="SomeObject">
SELECT something FROM somewhere WHERE id = #{id}
</select>
...
Now I have:
public interface Mapper {
#Select("SELECT something FROM somewhere WHERE id = #{id}")
public List<String> selectSomething(#Param("id") int id);
}
and it is working fine when I use it directly form java code.
But NOT working with camel flow
<to uri="mybatis:???...
There is no ID of select/statement which I can use.
This is not supported in camel-mybatis at this time of writing. But its a good idea to let us look into adding support for that. You are welcome to log a JIRA ticket at Apache Camel.

mybatis mapping the class which protobuf generated

How can I map the result to protobuf class in mybatis mapper? There is no set method in protobuf class. Should I generate the entity class by using newBuilder and build method?
Yes, this is possible. In When you are generating the model classes from your proto file each of them are having a Builder (that extends GeneratedMessageV3.Builder). So in your model definition you can add that, like this example:
#ResultType(MyProto.Builder.class)
This is working in fine when you are using ResultHandler, so there you can invoke the build method, like:
#Override
public void handleResult(ResultContext<? extends GeneratedMessageV3.Builder> resultContext) {
GeneratedMessageV3 proto = resultContext.getResultObject().build();
MyProto myproto = (MyProto)proto;
// do something with myproto
}
There is a trick. Let's say you have defined the following protobuf,
package com.example.protobuf;
message HelloMessage {
string id;
uint32 code;
}
You can have your HelloMapper like this,
<resultMap id="helloMessageMap" type="com.example.protobuf.HelloMessage">
<result column="id" property="id_"/>
<result column="code" property="code_"/>
</resultMap>
<select id="selectHelloMessage" resultMap="helloMessageMap">
select id, code from example
</select>
Please notice that id_ and code_ are used instead of id and code.

spring ehcache is not working

I am trying to implement ehcache to get static data (from table) loaded during application startup however when I make a call again to database, the call is going to database (can see running sql on console) instead of taking values from ehcache.
my code is:
ehcache.xml as below:
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd"
updateCheck="true"
monitoring="autodetect"
dynamicConfig="true">
<diskStore path="java.io.tmpdir" />
<cache name="ObjectList"
maxEntriesLocalHeap="10000"
maxEntriesLocalDisk="1000"
eternal="false"
diskSpoolBufferSizeMB="20"
timeToIdleSeconds="2000000" timeToLiveSeconds="900000000000"
memoryStoreEvictionPolicy="LFU"
transactionalMode="off">
<persistence strategy="localTempSwap" />
</cache>
</ehcache>
my repository class is:
public interface customRepository extends JpaRepository<Object, Long> {
#Cacheable(value = "ObjectList", cacheManager="abclCacheManager")
public Object findById(Long id);
#Cacheable(value = "ObjectList", cacheManager="abclCacheManager")
public List<Object> findAll();
}
and my cacheInitialiser class is:
#Configuration
#EnableCaching
#ComponentScan("com.abcl.process")
public class EhCacheConfiguration {
#Bean("abclCacheManager")
public CacheManager cacheManager() {
return new EhCacheCacheManager(ehCacheCacheManager().getObject());
}
#Bean
public EhCacheManagerFactoryBean ehCacheCacheManager() {
EhCacheManagerFactoryBean cmfb = new EhCacheManagerFactoryBean();
cmfb.setConfigLocation(new ClassPathResource("ehcache.xml"));
cmfb.setShared(true);
cmfb.setCacheManagerName("abclCacheManager");
return cmfb;
}
}
I am testing my this using below:
public class testCache {
doSomething() {
List<Object> listObject = repo.findAll();
listObject.size();
}
public void getOne() {
Object o = repo.findById(1L);
}
}
I can see a db hit in getAll method however I thought the results would get stored in cache and in the second call there would not be a db hit by method getById however I see a db hit on second call as well.
Can anyone please suggest if I am missing anything here.
When you cache the results of findAll it creates a single entry in the cache which maps the key generated by Spring caching, since your method has no parameter, to the List<Object>. It does not put into the cache one mapping per list element between id and the Object.
So when you use findById(Long), Spring caching will look for a cache entry mapping to the id. And since it cannot find one, it will hit the database.
There is no way of having Spring caching put one mapping per collection element. If that is really what you need, you will have to code it instead of relying on the #Cacheable annotation.

EclipseLink MOXy: Logical operators in XmlPath annotation

Do logical operators work in XmlPath annotations of EclipseLink MOXy?
I tried and could not make it work (no Exception is thrown and nothing is bound to "elements").
For example, I would like to have in a bindings file something like this:
<java-type name="Content">
<java-attributes>
<xml-element java-attribute="elements" xml-path="/a/b/ | /c/d"
type="ElementType" container-type="java.util.List" />
</java-attributes>
</java-type>
Is there a way to achieve the same result from a modification of the bindings without using the logical or in the xml-path?
I can only think of a workaround where one would use getters and settings in the domain model, bind both /a/b and /c/d to elements and have the setters append elements to the List rather then replacing the list upon each call to setElements(). I'd rather handle it in the bindings file, though.
Does there exist a place in the documentation that specifies which parts of XPath are supported in MOXy?
Here is an example of how you could support this use case.
Mapping Document (bindings.xml)
You could use the xml-elements mapping for this use case. On each of the nested xml-element mappings you would specify a different xml-path.
<?xml version="1.0"?>
<xml-bindings
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="forum17977009">
<java-types>
<java-type name="Content">
<xml-root-element/>
<java-attributes>
<xml-elements java-attribute="elements">
<xml-element xml-path="a/b"/>
<xml-element xml-path="c/d"/>
</xml-elements>
</java-attributes>
</java-type>
</java-types>
</xml-bindings>
Java Model (Content)
Below is the Java model we will use for this example.
package forum17977009;
import java.util.List;
public class Content {
private List<ElementType> elements;
public List<ElementType> getElements() {
return elements;
}
public void setElements(List<ElementType> elements) {
this.elements = elements;
}
}
jaxb.properties
To specify MOXy as your JAXB provider you include a file called jaxb.properties in the same package as your domain model with the following entry (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html).
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Input (input.xml)
Below is a sample input document.
<?xml version="1.0" encoding="UTF-8"?>
<content>
<a>
<b/>
<b/>
</a>
<c>
<d/>
<d/>
</c>
</content>
Demo
Below is some demo code you can run to prove that everything works:
package forum17977009;
import java.io.File;
import java.util.*;
import javax.xml.bind.*;
import org.eclipse.persistence.jaxb.JAXBContextProperties;
public class Demo {
public static void main(String[] args) throws Exception {
Map<String, Object> properties = new HashMap<String, Object>(1);
properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, "forum17977009/bindings.xml");
JAXBContext jc = JAXBContext.newInstance(new Class[] {Content.class}, properties);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum17977009/input.xml");
Content content = (Content) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(content, System.out);
}
}
Output
Since all of the items are of the same type, they will output based on the xml-path of the first xml-element in the xml-elements mapping:
<?xml version="1.0" encoding="UTF-8"?>
<content>
<a>
<b/>
<b/>
<b/>
<b/>
</a>
</content>
UPDATE
Does there exist a place in the documentation that specifies which
parts of XPath are supported in MOXy?
Here are some examples that should help:
http://blog.bdoughan.com/2010/07/xpath-based-mapping.html
http://blog.bdoughan.com/2011/03/map-to-element-based-on-attribute-value.html
http://blog.bdoughan.com/2010/09/xpath-based-mapping-geocode-example.html
We are going to add some validation on the XPath statements that are entered for mappings. You can track our progress on this using the following link:
http://bugs.eclipse.org/397101

Spring 3 and JAXB in Web Services

I'm trying to write a simple web service that automagically serializes and deserializes objects using JAXB:
#XmlRootElement
public class SimpleObject {
private int id;
private String name;
/* ... */
}
#Controller
public class SimpleObjectController {
#RequestMapping("submit",method=RequestMethod.POST)
public String doPost(#RequestBody SimpleObject value) {
return value.toString();
}
}
<?xml version="1.0"?>
<beans ...>
<oxm:jaxb2-marshaller id="marshaller" class="path.to.objects"/>
</beans>
When I actually make the request, however, I get the following:
HTTP Status 415
The server refused this request because the request entity is in a format not
supported by the requested resource for the requested method ().
I get no logs back from Spring, making it hard for me to determine the root cause. Is there a critical step in this process which I'm missing?
Typically this is because of a missing Accept header or Content Type header of "application/xml" in your request. Also if you are using Spring 3.1, you can make your annotation even more explicit this way:
#RequestMapping(value="submit",method=RequestMethod.POST, consumes="application/xml", produces="application/xml")

Resources