How to pull sub, sub objects with Spring WS and JAXB - spring-boot

I'm attempting to pull data from a SOAP service that I have no control over. The hierarchy contains ProductOrder -> ShipTo -> Item where there are one or more shipToes and one or more Items per shipto.
Their API uses a mock SQL like query language. I'm getting stack traces like the following when trying to pull data including the items. if I exclude item, I'm able to pull the ProductOrders along with ShipTo objects, but items is always an empty list.
java.lang.IllegalStateException: Failed to execute CommandLineRunner
at
org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:779)
~[spring-boot-1.5.0.RELEASE.jar:1.5.0.RELEASE] at
org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:760)
~[spring-boot-1.5.0.RELEASE.jar:1.5.0.RELEASE] at
org.springframework.boot.SpringApplication.afterRefresh(SpringApplication.java:747)
~[spring-boot-1.5.0.RELEASE.jar:1.5.0.RELEASE] at
org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
~[spring-boot-1.5.0.RELEASE.jar:1.5.0.RELEASE] at
edu.umich.oud.giftformatter.convioexport.Application.main(Application.java:39)
~[classes/:na] Caused by:
org.springframework.oxm.UncategorizedMappingException: Unknown JAXB
exception; nested exception is javax.xml.bind.JAXBException: Field
order for ShipTo.Item.ItemId does not match the schema definition for
record type ProductOrder at
org.springframework.oxm.jaxb.Jaxb2Marshaller.convertJaxbException(Jaxb2Marshaller.java:915)
~[spring-oxm-4.3.6.RELEASE.jar:4.3.6.RELEASE] at
edu.umich.oud.giftformatter.convioexport.CustJaxbUnMarshaller.unmarshal(CustJaxbUnMarshaller.java:37)
~[classes/:na] at
org.springframework.ws.support.MarshallingUtils.unmarshal(MarshallingUtils.java:62)
~[spring-ws-core-2.4.0.RELEASE.jar:2.4.0.RELEASE] at
org.springframework.ws.client.core.WebServiceTemplate$3.extractData(WebServiceTemplate.java:413)
~[spring-ws-core-2.4.0.RELEASE.jar:2.4.0.RELEASE] at
org.springframework.ws.client.core.WebServiceTemplate.doSendAndReceive(WebServiceTemplate.java:619)
~[spring-ws-core-2.4.0.RELEASE.jar:2.4.0.RELEASE] at
org.springframework.ws.client.core.WebServiceTemplate.sendAndReceive(WebServiceTemplate.java:555)
~[spring-ws-core-2.4.0.RELEASE.jar:2.4.0.RELEASE] at
org.springframework.ws.client.core.WebServiceTemplate.marshalSendAndReceive(WebServiceTemplate.java:390)
~[spring-ws-core-2.4.0.RELEASE.jar:2.4.0.RELEASE] at
org.springframework.ws.client.core.WebServiceTemplate.marshalSendAndReceive(WebServiceTemplate.java:383)
~[spring-ws-core-2.4.0.RELEASE.jar:2.4.0.RELEASE] at
edu.umich.oud.giftformatter.convioexport.services.ConvioClient.queryInternal(ConvioClient.java:159)
~[classes/:na] at
edu.umich.oud.giftformatter.convioexport.services.ConvioClient.query(ConvioClient.java:134)
~[classes/:na] at
edu.umich.oud.giftformatter.convioexport.services.ProductOrderService.getProductOrders(ProductOrderService.java:87)
~[classes/:na] at
edu.umich.oud.giftformatter.convioexport.services.ConvioService.load(ConvioService.java:82)
~[classes/:na] at
edu.umich.oud.giftformatter.convioexport.Application.lambda$runner$0(Application.java:72)
~[classes/:na] at
org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:776)
~[spring-boot-1.5.0.RELEASE.jar:1.5.0.RELEASE] ... 4 common frames
omitted Caused by: javax.xml.bind.JAXBException: Field order for
ShipTo.Item.ItemId does not match the schema definition for record
type ProductOrder ... 17 common frames omitted
The product order service contains a method like so:
public List<ProductOrderObj> getProductOrders(final Date startDate, final Date endDate) {
final String query = String.format("SELECT siteId,orderId,transactionId,purchaseAmount,taxDeductibleValue,\n" +
"shippingCharge,additionalDonation,discountAmount,discountCode,\n" +
"creationDate,createdBy,modifyDate,lastChangeBy,storeId,payment,\n" +
"purchaser,interactionSource,shipTo,\n" +
"receiptNumber,shipTo.item FROM ProductOrder where creationDate > %s and creationDate < %s",
convertDate(startDate), convertDate(endDate));
log.info("query is " + query);
final Session session = convioClient.startSession();
final ArrayList<ProductOrderObj> events = new ArrayList<>();
for (int page = 1; page < 100; page++) {
final List<? extends RecordObj> items = convioClient.query(session, page, ConvioConfiguration.MAX_DOWNLOADS_PER_REQUEST, query);
if (items.size() < ConvioConfiguration.MAX_DOWNLOADS_PER_REQUEST) {
events.addAll((List<ProductOrderObj>) items);
break;
}
events.addAll((List<ProductOrderObj>) items);
}
return events;
}
Which in turn calls the convioService.query method that effectively does this
private List<? extends RecordObj> queryInternal(final Session session, final
int page, final int pageSize, final String q) {
// setup query
final Query query = new Query();
query.setPage(BigInteger.valueOf(page));
query.setPageSize(BigInteger.valueOf(pageSize));
query.setQueryString(q);
log.trace(q);
// perform query
try {
final Object obj = getWebServiceTemplate().marshalSendAndReceive(query,
new SoapActionExecutionIdCallback(session));
final QueryResponse response = (QueryResponse) obj;
if (response != null) {
log.debug("Response was a " + response.getClass().getName());
return response.getRecord();
}
} catch (final Exception e) {
log.error(e.getMessage());
throw e;
}
throw new NullPointerException("response was null");
}

There seemed to be two issues causing this not to work:
Bad field definition for the child object. shipTo.items vs shipTo.Items
Disabling validation of the dtd in the marshaller/unmarshaller

Related

How to resolve ClassCastException in MultiResourceItemReader Spring Batch

I'm reading multiple files from the S3 bucket using MultiResourceItemReader, I'm getting ClassCastException before executing the myReader() method, Something wrong with MultiResourceItemReader not sure what's going wrong here.
Please find my code below:
#Bean
public MultiResourceItemReader<String> multiResourceReader()
{
String bucket = "mybucket;
String key = "/myfiles";
List<InputStream> resourceList = s3Client.getFiles(bucket, key);
List<InputStreamResource> inputStreamResourceList = new ArrayList<>();
for (InputStream s: resourceList) {
inputStreamResourceList.add(new InputStreamResource(s));
}
Resource[] resources = inputStreamResourceList.toArray(new InputStreamResource[inputStreamResourceList.size()]);
//InputStreamResource[] resources = inputStreamResourceList.toArray(new InputStreamResource[inputStreamResourceList.size()]);
// I'm getting all the stream content - I verified my stream is not null
for (int i = 0; i < resources.length; i++) {
try {
InputStream s = resources[i].getInputStream();
String result = IOUtils.toString(s, StandardCharsets.UTF_8);
System.out.println(result);
} catch (IOException e) {
e.printStackTrace();
}
}
MultiResourceItemReader<String> resourceItemReader = new MultiResourceItemReader<>();
resourceItemReader.setResources(resources);
resourceItemReader.setDelegate(myReader());
resourceItemReader.setDelegate((ResourceAwareItemReaderItemStream<? extends String>) new CustomComparator());
return resourceItemReader;
}
Exception:
Caused by: java.lang.ClassCastException: class CustomComparator cannot be cast to class org.springframework.batch.item.file.ResourceAwareItemReaderItemStream (CustomComparator and org.springframework.batch.item.file.ResourceAwareItemReaderItemStream are in unnamed module of loader org.springframework.boot.loader.LaunchedURLClassLoader #cc285f4)
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244)
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154)
... 65 common frames omitted
Can someone please help me to resolve this issue. Appreciated your help in advance. Thanks.
The reason you see the NullPointerException is due to the default comparator used by the MultiResourceItemReader to sort the resources after loading them.
The default compare behavior calls the getFilename() method of the InputStreamResource.
Refer - https://github.com/spring-projects/spring-batch/blob/115c3022147692155d45e23cdd5cef84895bf9f5/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/MultiResourceItemReader.java#L82
But the InputStreamResource just inherits the getFileName() method from its parent AbstractResource, which just returns null.
https://github.com/spring-projects/spring-framework/blob/316e84f04f3dbec3ea5ab8563cc920fb21f49749/spring-core/src/main/java/org/springframework/core/io/AbstractResource.java#L220
The solution is to provide a custom comparator for the MultiResourceItemReader. Here is a simple example, assuming you do not want to sort the resources in a specific way before processing:
public class CustomComparator implements Comparator<InputStream>{
#Override
public int compare(InputStream is1, InputStream is2) {
//comparing based on last modified time
return Long.compare(is1.hashCode(),is2.hashCode());
}
}
MultiResourceItemReader<String> resourceItemReader = new MultiResourceItemReader<>();
resourceItemReader.setResources(resources);
resourceItemReader.setDelegate(myReader());
//UPDATED with correction - set custom Comparator
resourceItemReader.setComparator(new CustomComparator());
Refer this answer for how a Comparator is used by Spring Batch MultiResourceItemReader.
File processing order with Spring Batch

Spring Boot QueryDSL BooleanExpression - "OR" condition depending on the value in table

I use Spring Boot and QueryDSL to combine a sql query and predicate. The problem is that I have to create a predicate to fetch data from table based on email BUT:
email can be in Freight.sender.email
OR in Freight.message.senderAddress
where Freight, Message, Sender are of course tables.
In table Freight we can have empty sender_id or message_id and depending on this I have to fetch rows by email from Freight.sender.email OR Freight.message.senderAddress (if Freight.sender is null)
Is it possible to create such a predicate that compares email from request query param with Freight.sender.email and only if Freight.sender.email doesn't exist, then my predicate shold search email in Freight.message.senderAddress
public Predicate build(Map<String, String> filters) {
return new OptionalBooleanBuilder(Expressions.asBoolean(true).isTrue())
.notNullAnd(qFreight.loadingAddress::containsIgnoreCase, filters.get(LOADING_ADDRESS))
.notNullAnd(qFreight.unloadingAddress::containsIgnoreCase, filters.get(UNLOADING_ADDRESS))
.notNullAnd(qFreight.loadingDate.eq(filters.get(LOADING_DATE) != null ? LocalDate.parse(filters.get(LOADING_DATE)) : now()), filters.get(LOADING_DATE))
.notNullAnd(qFreight.unloadingDate.eq(filters.get(UNLOADING_DATE) != null ? LocalDate.parse(filters.get(UNLOADING_DATE)) : now()), filters.get(UNLOADING_DATE))
//MY ATTEMPT - NOT WORKING:
.notNullAnd(qFreight.sender.email.eq(filters.get(SENDER_EMAIL)).or(qFreight.emailMessage.senderAddress.eq(SENDER_EMAIL)), filters.get(SENDER_EMAIL))
.build();
}
public class OptionalBooleanBuilder {
private BooleanExpression predicate;
public OptionalBooleanBuilder(BooleanExpression predicate) {
this.predicate = predicate;
}
public <T> OptionalBooleanBuilder notNullAnd(Function<T, BooleanExpression> expressionFunction, T value) {
if (nonNull(value)) {
return new OptionalBooleanBuilder(predicate.and(expressionFunction.apply(value)));
}
return this;
}
public BooleanExpression build() {
return predicate;
}
public <T>OptionalBooleanBuilder notNullAnd(BooleanExpression expression, T value) {
if(nonNull(value)){
return new OptionalBooleanBuilder(predicate.and(expression));
}
return this;
}
}
UPDATE
After suggestion from:
private Predicate addPredicate(OptionalBooleanBuilder builder, String email) {
if (nonNull(email)) {
return builder.notNullAnd(qFreight.sender.email.coalesce(qFreight.emailMessage.senderAddress.eq(email)).asBoolean(), email).build();
return builder.build();
}
I get error:
antlr.NoViableAltException: unexpected AST node: (
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.logicalExpr(HqlSqlBaseWalker.java:2169) ~[hibernate-core-5.4.17.Final.jar:5.4.17.Final]
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.logicalExpr(HqlSqlBaseWalker.java:2089) ~[hibernate-core-5.4.17.Final.jar:5.4.17.Final]
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.whereClause(HqlSqlBaseWalker.java:827) ~[hibernate-core-5.4.17.Final.jar:5.4.17.Final]
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.query(HqlSqlBaseWalker.java:621) ~[hibernate-core-5.4.17.Final.jar:5.4.17.Final]
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.selectStatement(HqlSqlBaseWalker.java:325) ~[hibernate-core-5.4.17.Final.jar:5.4.17.Final]
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.statement(HqlSqlBaseWalker.java:273) ~[hibernate-core-5.4.17.Final.jar:5.4.17.Final]
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:276) ~[hibernate-core-5.4.17.Final.jar:5.4.17.Final]
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:192) ~[hibernate-core-5.4.17.Final.jar:5.4.17.Final]
2020-12-24 00:45:35.535 ERROR 16068 --- [nio-9090-exec-8] p.a.m.s.filter.JwtAuthorizationFilter : Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected AST node: ( near line 3, column 52 [select freight
from pl.appwise.mtf.freight.domain.model.Freight freight
where ?1 = ?1 and freight.user.id = ?2 and coalesce(freight.sender.email, freight.emailMessage.senderAddress = ?3)
order by freight.loadingDate desc]; nested exception is java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected AST node: ( near line 3, column 52 [select freight
from pl.appwise.mtf.freight.domain.model.Freight freight
where ?1 = ?1 and freight.user.id = ?2 and coalesce(freight.sender.email, freight.emailMessage.senderAddress = ?3)
order by freight.loadingDate desc]
2020-12-24 00:45:37.906 INFO 16068 --- [ scheduling-1] ilAccoun
Use Freight.sender.email.coalesce(Freight.message.senderAddress). Freight.message.senderAddress will be NULL if Freight.message is NULL. For optional associations left outer joins are used by default, so this should cause no issue. Otherwise, explicitly use a left join yourself.

How to listen to multiple topics by multiple StreamListener Spring Cloud Stream and Kafka stream

I want to listen to two Kafka topics like in the code below and there are two source events that need to be handled and transform to another event.
So I want to listen these two event in one EnableBinding
#EnableBinding(PartnerOrderCancelledEventImporter.Targets.class)
public class PartnerOrderCancelledEventImporter {
#StreamListener(Targets.INPUT_ORDER_CANCELLED)
#SendTo(Targets.OUTPUT)
public KStream<?, TriggeringEvent> processOrderCancelled(KStream<?, OrderCancelledV1> input) {
LogInfo("PartnerOrderCancelled-OrderCancelledV1 stream started");
LogInfo(" KafkaBrokers: " + KafkaBrokers);
return input
.filter((key, value) -> IsFFFaultAndNoRoutes(value))
.peek((key, value) -> LogInfo("OrderCancelle", new LogObject(value)))
.map((key, value) -> KeyValue.pair(key, new TriggeringEvent(value)));
}
#StreamListener(Targets.INPUT_ORDER_ITEM_STOCK_CHECKED)
#SendTo(Targets.OUTPUT)
public KStream<?, TriggeringEvent> processOrderItemStockChecked(KStream<?, OrderItemStockCheckedV1> input) {
LogInfo("PartnerOrderCancelled-OrderItemStockCheckedV1 stream started");
LogInfo(" KafkaBrokers: " + KafkaBrokers);
return input
.filter((key, value) -> IsItemNoStock(value))
.peek((key, value) -> LogInfo("OrderItemStockChecke", new LogObject(value)))
.map((key, value) -> KeyValue.pair(key, new TriggeringEvent(value)));
}
public interface Targets {
String INPUT_ORDER_CANCELLED = "partnerOrderCancelledInputOrderCancelled";
String INPUT_ORDER_ITEM_STOCK_CHECKED = "partnerOrderCancelledInputOrderItemStockChecked";
String OUTPUT = "triggeringEventsOutputPartnerOrderCancelled";
#Input(INPUT_ORDER_CANCELLED)
KStream<?, ?> inputOrderCancelled();
#Input(INPUT_ORDER_ITEM_STOCK_CHECKED)
KStream<?, ?> inputOrderItemStockChecked();
#Output(OUTPUT)
KStream<?, ?> output();
}
spring.cloud.stream.bindings.triggeringEventsOutputPartnerOrderCancelled.destination=dev.comms.triggeringevents.TriggeringEvent-events-1.0
spring.cloud.stream.bindings.partnerOrderCancelledInputOrderItemStockChecked.destination=dev.ecom.order-management-service.order-item-stock-checked-events-v1
spring.cloud.stream.bindings.partnerOrderCancelledInputOrderItemStockChecked.group=TriggeringEvents-PartnerOrderCancelled
spring.cloud.stream.bindings.partnerOrderCancelledInputOrderCancelled.destination=dev.ecom.order-management-service.order-cancelled-events-v1
spring.cloud.stream.bindings.partnerOrderCancelledInputOrderCancelled.group=TriggeringEvents-PartnerOrderCancelled
thrown below exceptions
org.springframework.beans.factory.BeanInitializationException: Cannot setup StreamListener for public org.apache.kafka.streams.kstream.KStream com.farfetch.communication.triggeringeventsimporter.PartnerOrderCancelledEventImporter.processOrderItemStockChecked(org.apache.kafka.streams.kstream.KStream); nested exception is java.lang.reflect.UndeclaredThrowableException
at org.springframework.cloud.stream.binder.kafka.streams.KafkaStreamsStreamListenerSetupMethodOrchestrator.orchestrateStreamListenerSetupMethod(KafkaStreamsStreamListenerSetupMethodOrchestrator.java:195)
at org.springframework.cloud.stream.binding.StreamListenerAnnotationBeanPostProcessor.doPostProcess(StreamListenerAnnotationBeanPostProcessor.java:195)
at org.springframework.cloud.stream.binding.StreamListenerAnnotationBeanPostProcessor.lambda$postProcessAfterInitialization$0(StreamListenerAnnotationBeanPostProcessor.java:167)
at java.lang.Iterable.forEach(Iterable.java:75)
at org.springframework.cloud.stream.binding.StreamListenerAnnotationBeanPostProcessor.injectAndPostProcessDependencies(StreamListenerAnnotationBeanPostProcessor.java:285)
at org.springframework.cloud.stream.binding.StreamListenerAnnotationBeanPostProcessor.afterSingletonsInstantiated(StreamListenerAnnotationBeanPostProcessor.java:105)
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
at org.springframework.cloud.stream.binder.kafka.streams.KStreamStreamListenerResultAdapter.adapt(KStreamStreamListenerResultAdapter.java:41)
at org.springframework.cloud.stream.binder.kafka.streams.KStreamStreamListenerResultAdapter.adapt(KStreamStreamListenerResultAdapter.java:31)
at org.springframework.cloud.stream.binder.kafka.streams.KafkaStreamsStreamListenerSetupMethodOrchestrator.orchestrateStreamListenerSetupMethod(KafkaStreamsStreamListenerSetupMethodOrchestrator.java:187)
... 39 more
Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.cloud.stream.binder.kafka.streams.KStreamBoundElementFactory$KStreamWrapperHandler.invoke(KStreamBoundElementFactory.java:99)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
... 43 more
Caused by: java.lang.IllegalArgumentException: delegate already set to org.apache.kafka.streams.kstream.internals.KStreamImpl#491cafec
You are getting this exception because you are trying to bind the same outbound KStream from both processors (output). Is there any chance that you can add another output binding for the second processor? That should address this particular error that you are getting. On the other hand, if this is a requirement that your use case demands, then currently the binder doesn't support that (It could be a good feature to add though). As a workaround, you could make the second processor returns nothing and then call the to method on KStream to send it manually.

How to write two parasmeters in Spring MyBatis Mapper?

I'm currently implements Comments on comments.
When a member views a given post, they should be able to see comments on comments and comments.
To satisfy the above conditions, you need the index value of the post and the index value of the comment. That is, two parameter values ​​are required.
So I wrote the following logic.
boardDaoMapper.xml
<select id="selectCommentOfComment" parameterType="java.util.HashMap" resultType="java.util.HashMap">
select * from board_comment_of_comment where board_idx = #{board_idx} and board_comm_idx = #{board_comm_idx}
</select>
And the BoardDao code:
public List<HashMap<String, Object>> selectCommentOfComment(int board_idx, int board_comm_idx);
And the BoardService:
#Override
public List<HashMap<String, Object>> getBoardCommentOfComment(int board_idx, int board_comm_idx)
{
System.out.println("ServieImple getBoardCommentOfComment Function Call >> " + board_idx + " / " + board_comm_idx);
List<HashMap<String, Object>> c_of_c_list = boardDao.selectCommentOfComment(board_idx, board_comm_idx);
return c_of_c_list;
}
And BoardController code:
#RequestMapping("viewPage.do")
public String viewPage(HttpSession session,HttpServletRequest request, HttpServletResponse response,Model model,
#RequestParam(value="board_idx", defaultValue="1") int board_idx) throws IOException,IllegalStateException
{
checkLogin(request, response, session);
// 해당 게시물에 대한 댓글들의 idx값 조회 (board_comm_idx)
List<HashMap<String, Object>> commentList= bService.getBoardForComment(boardData.getBoard_idx());
System.out.println("Board_idx Value was : " + board_idx);
// 해당 게시물에 대한 댓글들의 개수만큼 for문을 돌고..
for(int i = 0; i < commentList.size(); i++)
{
// board_comm_idx 값을 변수에 저장
int board_comm_idx= (Integer) commentList.get(i).get("board_comm_idx");
List<HashMap<String, Object>> getCommentOfComentList =
bService.getBoardCommentOfComment(board_idx, board_comm_idx);
System.out.println("GetCommentOfCommentList : " + getCommentOfComentList);
model.addAttribute("cocList", getCommentOfComentList);
}
return "viewPage";
}
Values ​​are good for two parameters.(at BoardService) Like that..
ServieImple getBoardCommentOfComment Function Call >> 2 / 3
However, this does not perform well and causes an error.
org.apache.ibatis.binding.BindingException: Parameter 'board_idx' not found. Available parameters are [0, 1, param1, param2]
at org.apache.ibatis.binding.MapperMethod$ParamMap.get(MapperMethod.java:165)
at org.apache.ibatis.reflection.wrapper.MapWrapper.get(MapWrapper.java:44)
at org.apache.ibatis.reflection.MetaObject.getValue(MetaObject.java:116)
at org.apache.ibatis.executor.BaseExecutor.createCacheKey(BaseExecutor.java:186)
at org.apache.ibatis.executor.CachingExecutor.createCacheKey(CachingExecutor.java:129)
at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:76)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:108)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:102)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:358)
at com.sun.proxy.$Proxy11.selectList(Unknown Source)
at org.mybatis.spring.SqlSessionTemplate.selectList(SqlSessionTemplate.java:198)
at org.apache.ibatis.binding.MapperMethod.executeForMany(MapperMethod.java:119)
at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:63)
at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:52)
at com.sun.proxy.$Proxy13.selectCommentOfComment(Unknown Source)
at service.BoardServiceImple.getBoardCommentOfComment(BoardServiceImple.java:367)
at controller.BoardController.viewPage(BoardController.java:175)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:180)
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:440)
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:428)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:635)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
I think Mapper has a problem.
Why is this happening and how should I fix it?
Your comments are valuable. Please tell me your opinion.
The problem with the current approach
You currently your mapping set up in a way that doesn't accept your parameters into the DAO layer parameterType="java.util.HashMap"
This being the case, iBatis is expecting a HashMap to be passed.
There are multiple ways that you can go about fixing your code.
1.) Using Named Parameters (Depending on your version) > 3
public List<HashMap<String, Object>> selectCommentOfComment(#Param("board_idx") int board_idx, #Param("board_comm_idx") int board_comm_idx);
And remove the parameter type from your mapping xml.
2.) Pass in the expected object
You can fix your DAO method signature to reflect your mapping. And make the corresponding changes in the service.
DAO:
public List<HashMap<String, Object>> selectCommentOfComment(HashMap<String, Object> parameter);
SERVICE:
public List<HashMap<String, Object>> getBoardCommentOfComment(int board_idx, int board_comm_idx)
{
HashMap<String, Object> parameters = new HashMap<String, Object>();
parameters.put("board_idx", board_idx);
parameters.put("board_comm_idx", board_comm_idx");
List<HashMap<String, Object>> c_of_c_list = boardDao.selectCommentOfComment(board_idx, board_comm_idx);
return c_of_c_list;
}
You can draw additional inspiration from their documentation.
http://www.mybatis.org/mybatis-3/sqlmap-xml.html

Spring4 + and Hibernate 4 Transactions

Using spring 4.0.6.RELEASE, Hibernate 4.3.6.Final and hsqldb 2.3.2. My integration test looks like the following;
#Test(expected = DataIntegrityViolationException.class)
public final void testDuplicateItems() {
final ServerEntity serverEntity1 = new ServerEntity("DuplicateItem");
opService.save(serverEntity1);
opService.save(serverEntity1);
}
This works as expected. However, when I run my standalone java component i can save the first item, the second item which is a duplicate is not saved but Im unable to catch the exception. Here is the log file
WARN org.hibernate.engine.jdbc.spi.SqlExceptionHelper: SQL Error: -104, SQLState: 23505
2014-08-27 14:52:06,843 ERROR org.hibernate.engine.jdbc.spi.SqlExceptionHelper: integrity constraint violation: unique constraint or index violation; UK_NFU7LXMMDFVIR1WD08662085N table: SERVERENTITY
[WARNING]
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.codehaus.mojo.exec.ExecJavaMojo$1.run(ExecJavaMojo.java:293)
at java.lang.Thread.run(Thread.java:745)
Caused by: org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [UK_NFU7LXMMDFVIR1WD08662085N]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
at org.springframework.orm.hibernate4.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:161)
at org.springframework.orm.hibernate4.HibernateTransactionManager.convertHibernateAccessException(HibernateTransactionManager.java:681)
at org.springframework.orm.hibernate4.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:563)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:757)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:726)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:478)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:272)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy38.execute(Unknown Source)
at com.opserver.simpleapp.MainApp.start(MainApp.java:60)
at com.opserver.simpleapp.MainApp.main(MainApp.java:37)
... 6 more
Both the service and dao implementations have #Transactional at class level. I've a component class that is calling the service class, this component class is not transactional! The component class prints a response, does the session need to be flushed here?
Need to figure out why the save method in the dao is not throwing the exception, I can actually see it an id being created and then rolled back.
J
My component class is very basic;
boolean isValid = opServerService.loadXMLFile("Server.xml");
try{
if (isValid) {
System.out.println("Entity has been added");
} else {
System.out.println("Entity has not been added");
}
}catch (Exception ex){
System.out.println("that was a focked up");
}
The problem is that "Entity has been added" gets printed to console and then I see the above error in console.
DAO looks like this
#Override
#Transactional
public final void save(final ServerEntity serverEntity) throws DataIntegrityViolationException {
LOGGER.debug(">>start(serverEntity=" + serverEntity + ")");
Preconditions.checkNotNull(serverEntity);
this.getCurrentSession().save(serverEntity);
}
Service method with #Transactional at class level, looks like this
#Override
public final void save(ServerEntity serverEntity) {
opServerDao.save(serverEntity);
}
And Component looks like this
#Component
public class AddCommand implements Command {
#Autowired
OpService opService;
public AddServerCommand() {
super();
}
#Override
public void execute(String[] options) {
try{
boolean isValid = opService.save("Server.xml");
if (isValid) {
System.out.println("Entity has been added");
} else {
System.out.println("Entity has not been added");
}
}catch (Exception ex){
System.out.println("Exception found");
}
}
}
You should catch your exception in the component that's calling the service.
Found the solution, was missing from the applicationContext.xml. The wrapped try/catch around the opService now catches the exception. Need to implement my own custom exception handler but for now at least I know the component class is handling the exception which is being thrown from the service class.
Thanks for your help.
J

Resources