MyBatis select statement returns null values - jdbc

I'm trying to run a simple MyBatis example, selecting all rows from the "trains" table.
The problem is that the query performs, but it returns a list with the correct number of elements, but populated with null values.
The same query runned directly with JDBC PreparedStatement works fine.
Perhaps it's a configuration problem, but I cannot figure out what I'm doing wrong.
Here is the code. Thanks in advance.
Train.java
package org.example.mybatis.domain;
public class Train implements Serializable
{
private int id;
private String type;
// getters and setters
}
TrainMapper.java
package org.example.mybatis.persistence;
public interface TrainMapper {
List<Train> getAllTrains();
}
TrainSelector.java
package org.example.mybatis.test;
public class TrainSelector implements TrainMapper {
private static String resource = "mybatis-config.xml";
private static SqlSessionFactory factory = null;
private SqlSessionFactory getSqlSessionFactory()
{
if (factory == null)
{
try {
InputStream inputStream = Resources.getResourceAsStream(resource);
factory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
return factory;
}
#Override
public List<Train> getAllTrains()
{
List<Train> trains = null;
SqlSession session = getSqlSessionFactory().openSession();
try {
TrainMapper mapper = session.getMapper(TrainMapper.class);
trains = mapper.getAllTrains();
} finally {
session.close();
}
return trains;
}
public static void main(String[] args) {
List<Train> trains = null;
TrainSelector trainSelector = new TrainSelector();
trains = trainSelector.getAllTrains();
System.out.println(trains);
}
}
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="database.properties" />
<typeAliases>
<typeAlias alias="Train" type="org.example.mybatis.domain.Train" />
<!--package name="org.example.mybatis.domain" />-->
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${database.driver}" />
<property name="url" value="${database.url}" />
<property name="username" value="${database.username}" />
<property name="password" value="${database.password}" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="org/example/mybatis/persistence/TrainMapper.xml" />
</mappers>
</configuration>
TrainMapper.xml
<?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.example.mybatis.persistence.TrainMapper">
<cache />
<select id="getAllTrains" parameterType="list" resultType="Train">
SELECT *
FROM trains
</select>
</mapper>
JdbcStatementExample.java
package org.example.mybatis.test;
public class JdbcStatementExample {
private static void selectAllTrains() throws SQLException
{
String sql = "SELECT * FROM trains";
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
String url = "jdbc:mysql://localhost/testing";
String user = "test";
String password = "test";
try {
conn = DriverManager.getConnection(url, user, password);
ps = conn.prepareStatement(sql);
rs = ps.executeQuery();
while (rs.next()) {
String id = rs.getString("train_id");
String type = rs.getString("train_type");
System.out.println("id: " + id);
System.out.println("type: " + type);
}
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
if (ps != null) {
ps.close();
}
if (conn != null) {
conn.close();
}
}
}
public static void main(String[] args)
{
try {
selectAllTrains();
} catch (SQLException e) {
e.printStackTrace();
}
}
}

The names of the columns in the result set are different from the names of the properties in the Train object. You need an explicit result map to let Mybatis know which column is to be mapped to which property.
<resultMap id="trainMap" type="Train>
<id property="id" column="train_id" javaType="java.lang.Integer" jdbcType="INTEGER"/>
<result property="type" column="train_type" javaType="java.lang.String" jdbcType="VARCHAR"/>
</resultMap>
Making your select element into
<select id="getAllTrains" parameterType="list" resultType="trainMap">
SELECT * FROM trains
</select>

Other option is to use column names an aliases.
The column names will be your database's and the aliases will be set to match with your Train object properties:
<select id="getAllTrains" parameterType="list" resultType="trainMap">
SELECT
train_id as id,
train_type as type
FROM trains
</select>

I had the same problem, but only for fields with multiple words. Of course my naming convention in SQL was user_id and in java was userId. This piece of config inside my mybatis-config.xml file saved the day:
<settings>
<setting name="mapUnderscoreToCamelCase" value="false"/>
</settings>
or for properties file:
mybatis.configuration.map-underscore-to-camel-case=true
credit: https://chois9105.github.io/spring/2017/12/31/configuring-mybatis-underscore-to-camel-case.html

Results can be mapped as described by Seeta or in the official docs here:
https://mybatis.org/mybatis-3/sqlmap-xml.html
In MyBatis 3.x the example doesn't work as you need to set resultMap rather than resultType. And you must not set both at the same time! Working example looks like:
<select id="getAllTrains" parameterType="list" resultMap="trainMap">
SELECT * FROM trains
</select>

if you are using spring boot, you can change the map-underscore-to-camel-case property as true like below. because most if the time we use _ (user_id) when create the table attributes. but in java we use camelCase (userId) for the variables. then mybatis don't know about that and when it tries to mapping, the error is thrown.
mybatis.configuration.map-underscore-to-camel-case=true

Related

Mybatis spring boot how to create dynamic update query

I have given the mybatis update qry below, In which update query specified in a static way. In my case i have to make it dynamic based on the incoming fields.
#Update("UPDATE guestpayment SET " +
"SourceSystemUpdated=#{sourcesysupdated}," +
"SourceSystemUpdateComment=#{sourcesysupdatedcomments}" +
"WHERE PrimaryId=#{PrimaryId}")
void updateguestpayment(Guestpayment updateguestpayment);
How to make the update query dynamic based on the incoming fields as jsonobject?
Mapper xml object
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mybatis.mybatis.dao.GuestpaymentDAO">
<resultMap id="Guestpayment" type="com.mybatis.mybatis.models.Guestpayment" >
<id property="PrimaryId" column="PrimaryId" javaType="int" jdbcType="integer" />
<result property="SourceSystemUpdated" column="SourceSystemUpdated" javaType="string" jdbcType="VARCHAR" />
<result property="SourceSystemUpdateComment" column="SourceSystemUpdateComment" javaType="string" jdbcType="VARCHAR" />
</resultMap>
<update id="updateguestpayment">
update guestpayment as g
<set >
<if test="updateguestpayment.SourceSystemUpdated != null and updateguestpayment.SourceSystemUpdated != ''" >
g.SourceSystemUpdated = #{updateguestpayment.SourceSystemUpdated} ,
</if>
<if test="updateguestpayment.SourceSystemUpdateComment != null and updateguestpayment.SourceSystemUpdateComment != ''">
<!-- No need to deal with commas, <set> will auto delete extra commas -->
g.SourceSystemUpdateComment = #{updateguestpayment.SourceSystemUpdateComment},
</if>
</set>
where g.PrimaryId = #{updateguestpayment.PrimaryId}
</update>
</mapper>
Guestpayment.java
package com.mybatis.mybatis.models;
import java.util.Date;
public class Guestpayment {
private int PrimaryId;
private String sourcesysupdated;
private String sourcesysupdatedcomments;
public int getPrimaryId() {
return PrimaryId;
}
public void setPrimaryId(int primaryId) {
PrimaryId = primaryId;
}
public String getSourcesysupdated() {
return sourcesysupdated;
}
public void setSourcesysupdated(String sourcesysupdated) {
this.sourcesysupdated = sourcesysupdated;
}
public String getSourcesysupdatedcomments() {
return sourcesysupdatedcomments;
}
public void setSourcesysupdatedcomments(String sourcesysupdatedcomments) {
this.sourcesysupdatedcomments = sourcesysupdatedcomments;
}
}
GuestpaymentDAO(interface)
#Mapper
#Repository
public interface GuestpaymentDAO {
void updateguestpayment(#Param("updateguestpayment") Guestpayment updateguestpayment);
}
You can use Criteria to create a dynamic update query. In your case, you can do it like below,
public class GuestPaymentDAO {
#PersistenceContext
private EntityManager em;
public void updateGustPayment(String sourcesysupdated, String sourcesysupdatedcomments, int PrimaryId) {
CriteriaBuilder cb = this.em.getCriteriaBuilder();
// create update
CriteriaUpdate<Guestpayment> update = cb.createCriteriaUpdate(Guestpayment.class);
// set the root class
Root e = update.from(Guestpayment.class);
// set update clause
if (sourcesysupdated != null) {
update.set("SourceSystemUpdated", sourcesysupdated);
}
if (sourcesysupdatedcomments != null) {
update.set("SourceSystemUpdateComment", sourcesysupdatedcomments);
}
// set where clause
update.where(cb.equal(e.get("PrimaryId"), PrimaryId));
// perform update
this.em.createQuery(update).executeUpdate();
}
}
Why not use mybatis xml and entity to cooperate with each other?
eg :
DAO
#Mapper
#Repository
public interface GuestpaymentDAO {
void updateguestpayment(#Param("updateguestpayment") Guestpayment updateguestpayment);
}
mybatis mapper xml file: GuestpaymentDAO.xml
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.dao.GuestpaymentDAO">
<resultMap id="Guestpayment" type="com.example.demo.entity.Guestpayment" >
<id property="PrimaryId" column="PrimaryId" javaType="string" jdbcType="VARCHAR" />
<result property="SourceSystemUpdated" column="SourceSystemUpdated" javaType="string" jdbcType="VARCHAR" />
<result property="SourceSystemUpdateComment" column="SourceSystemUpdateComment" javaType="string" jdbcType="VARCHAR" />
</resultMap>
<update id="updateguestpayment">
update Guestpayment as g
<set >
<if test="updateguestpayment.SourceSystemUpdated != null and updateguestpayment.SourceSystemUpdated != ''" >
g.SourceSystemUpdated = #{updateguestpayment.SourceSystemUpdated} ,
</if>
<if test="updateguestpayment.SourceSystemUpdateComment != null and updateguestpayment.SourceSystemUpdateComment != ''">
<!-- No need to deal with commas, <set> will auto delete extra commas -->
g.SourceSystemUpdateComment = #{updateguestpayment.SourceSystemUpdateComment},
</if>
</set>
where g.PrimaryId = #{updateguestpayment.PrimaryId}
</update>
</mapper>
doc: https://mybatis.org/mybatis-3/sqlmap-xml.html
src:src image

Hibernate ClassCastException when retrieving a class from query.getResultList

I created a small SpringBoot App which is used together with Hibernate to work with our Oracle Databse.
But I ran into the following Problem:
Whenever I load an Object from the database with SessionFactory.createQuery() and then query.getResultList(). I do get a List of Results with the correct Class annotations (when looking at the code in debug mode). But I cannot do MyClass x = list.get(0), even though the list is a List of MyClass. I can only get an Object but cannot "cast" to the correct class.
Uidnr is a simple Class with no join tables or any other dependencies on other tables form the database. Its only BigDecimal's, String's and Timestamp's.
Here is the code and configs of everything:
POST Endpoint:
#RequestMapping(value = "/e10", method = RequestMethod.POST, produces = MediaType.APPLICATION_XML_VALUE)
#ResponseBody
public String e10() {
Database db = new Database();
List<Uidnr> bel = db.getUidnrList(new BigDecimal("1009316"));
Uidnr element = bel.get(0); //CLASSCASTEXCEPTION HERE!!!
return element.getNr();
}
Method to get the Class from Database:
public List<Uidnr> getUidnrList(BigDecimal id) {
SessionFactory factory = null;
List<Uidnr> uidnr = new ArrayList<Uidnr>();
Session session = null;
try {
factory = getSessionFactory();
session = factory.openSession();
Query<Uidnr> query = session.createQuery("from Uidnr where adrid=:sblid", Uidnr.class);
query.setParameter("sblid", id);
uidnr = query.getResultList(); //This is a List<Uidnr
} catch (HibernateException ex) {
logger.error("Error loading Sendungen.", ex);
} finally {
close(factory, session);
}
return uidnr;
}
SessionFactory:
private SessionFactory getSessionFactory() {
SessionFactory sf = null;
File optextconf = new File("conf/hibernate.cfg.xml");
Configuration c = new Configuration();
if (optextconf.exists()) {
c.configure(optextconf);
c.addResource("eu/lbase/invsvc/app/model/internal/Uidnr.hbm.xml");
sf = c.buildSessionFactory();
logger.info("Session factory loaded from external file {}.", optextconf.getAbsolutePath());
} else {
logger.error("Configuration {} not found. Can not connect to database.", optextconf.getAbsolutePath());
}
return sf;
}
Uidnr.hbm.xml file located under src/main/resources, in a package called eu.lbase.invsvc.app.model.internal:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="eu.lbase.invsvc.app.model.internal.Uidnr" table="SUID_UIDNR">
<composite-id>
<key-property name="adrid" column="UID_ADRID" />
<key-property name="staid" column="UID_STAID" />
</composite-id>
<property name="nr" column="UID_NR"/>
<property name="stnr" column="UID_STNR"/>
<property name="deflt" column="UID_DEFLT"/>
<property name="aend" column="UID_AEND"/>
<property name="usrid" column="UID_USRID"/>
<property name="stktonr" column="UID_STKTONR"/>
<property name="zoktonr" column="UID_ZOKTONR"/>
<property name="vollmacht" column="UID_VOLLMACHT"/>
</class>
</hibernate-mapping>
Exception:
java.lang.ClassCastException: eu.lbase.invsvc.app.model.internal.Uidnr cannot be cast to eu.lbase.invsvc.app.model.internal.Uidnr
at eu.lbase.invsvc.app.controller.WebController.e10(WebController.java:91) ~[main/:na]
Some Tests:
System.out.println(bel.getClass()); //class java.util.ArrayList
Object test = bel.get(0);
System.out.println(test.getClass()); //class eu.lbase.invsvc.app.model.internal.Uidnr
System.out.println(bel.get(0) instanceof eu.lbase.invsvc.app.model.internal.Uidnr); //false

Caused by: org.hibernate.QueryException: Not all named parameters have been set: while using hibernate template

Getting this exception while using hibernate template in spring.
Caused by: org.hibernate.QueryException: Not all named parameters have been set: [roledesc, sapid, pass] [
select u from Role as r left join r.users as u where u.sapid=:sapid and u.pass=:pass and r.roledesc=:roledesc
]
at org.hibernate.impl.AbstractQueryImpl.verifyParameters(AbstractQueryImpl.java:291)
at org.hibernate.impl.AbstractQueryImpl.verifyParameters(AbstractQueryImpl.java:275)
at org.hibernate.impl.QueryImpl.list(QueryImpl.java:75)
at org.springframework.orm.hibernate3.HibernateTemplate$33.doInHibernate(HibernateTemplate.java:988)
at org.springframework.orm.hibernate3.HibernateTemplate$33.doInHibernate(HibernateTemplate.java:1)
at org.springframework.orm.hibernate3.HibernateTemplate.doExecute(HibernateTemplate.java:406)
... 30 more
Here is the class where I'm trying to fetch the named query:
public class LoginDaoImpl extends AbstractDaoImpl implements LoginDao {
private static final Logger LOGGER = Logger.getLogger(LoginDaoImpl.class);
#Override
public User loginCheck(User user, Role role) {
LOGGER.debug("Inside validate user:" + user.getPass());
user.setName("");
List<User> employee = new ArrayList<User>();
Query query = (Query) template.findByNamedQuery("findRoleforaUser");
query.setString("sapid", user.getSapid());
query.setString("pass", user.getPass());
query.setString("roledesc", role.getRoledesc());
employee = query.list();
if(employee == null)
{
user.setName("");
}
else if(employee.isEmpty())
{
user.setName("");
}
else if (!(user.getPass().equals(employee.get(0).getPass())))
LOGGER.info("No match found!");
else {
user.setName( employee.get(0).getName());
LOGGER.debug("\nUser \"" + employee.get(0).getName()
+ "\" found and login is successful ");
}
return user;
}
}
And here is my .hbm file contents where the named query is defined:
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-mapping SYSTEM "D:\My HCL JEE Progs\CBA_Quiz\src\dtd\hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.hcl.cba.payments.domain.Role" table="roles">
<id name="roleid" type="int" column="ROLEID" />
<property name="roledesc" type="string" column="ROLEDESC" />
<set name="users" cascade="all">
<key column="roleid" />
<one-to-many class="com.hcl.cba.payments.domain.User" />
</set>
</class>
<query name="findRoleforaUser">
<![CDATA[select u from Role as r left join r.users as u where u.sapid=:sapid and u.pass=:pass and r.roledesc=:roledesc]]>
</query>
</hibernate-mapping>
P.S:I tried running/accessing the query without hibernate template and it ran fine and I had set the variables the same way too:
Query query = session.findByNamedQuery("findRoleforaUser");
query.setString("sapid", user.getSapid());
query.setString("pass", user.getPass());
query.setString("roledesc", role.getRoledesc());
employee =query.list();
This should work
List<User> employees = new ArrayList<User>();
String[] paramNames = { "sapid", "pass", "roledesc" };
Object[] values = { user.getSapid(), user.getPass(), role.getRoledesc() };
employees = template.findByNamedQueryAndNamedParam("findRoleforaUser", paramNames, values);

How to Select a BLOB column from database using iBatis

One of a table's column is of BLOB datatype (Oracle 10g). We have a simple select query executed via iBatis to select the BLOB column and display it using Struts2 & JSP.
The result tag in the iBatis xml file had the jdbctype as java.sql.Blob
<result property="uploadContent" column="uploadcontent" jdbctype="Blob"/>
Should we be mentioning any typeHandler class for Blob column ? Currently we are getting an error stating column type mismatch.
Note: This column is selected and mapped into a java bean who has an attribute of type java.sql.Blob
I think you cannot use native jdbctype for LOB types in Oracle with iBatis. The solution is to create custom typeHandler to handle LOB and then map it like -
<result property="aClassStringProperty" column="aClobColumn" typeHandler="com.path.to.my.ClobTypeHandler"/>
More information on typeHandlerCallback here.
It is not neccesary to create a typeHandler. For Oracle, the jdbctype is BLOB
<result property="bytes" column="COLUMNBLOB" jdbcType="BLOB" />
Assumming "bytes" as byte [].
The important thing: in the select sql, you must set the jdbcType in this way:
INSERT INTO X (COLUMNBLOB) VALUES #bytes:BLOB#
I noticed that this jdbctype for Postgresql is different. You must set:
<result property="bytes" column="COLUMNBLOB" jdbcType="BINARY" />
I found somebody who deal with this here.
For a CLOB :
<result property="uploadContent" column="obfile" jdbctype="String" />
For a BLOB :
<result property="uploadContent" column="obfile" jdbctype="byte[]" />
I am still looking for it to work with C# !
I dindn't have problems using INSERTs, my problems where when I did SELECT of the blob type. I am using Oracle 9i and this is how I've done:
Add the Oracle JDBC driver to your project, you will need mybatis dependencies too. If you are using Maven:
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc14</artifactId>
<version>10.2.0.3.0</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.3</version>
</dependency>
Add the custom BaseTypeHandler for reading byte[] from Oracle BLOB class:
#MappedTypes(byte[].class)
public class OracleBlobTypeHandler extends BaseTypeHandler<byte[]> {
#Override
public void setNonNullParameter(PreparedStatement preparedStatement, int i, byte[] bytes, JdbcType jdbcType) throws SQLException {
// see setBlobAsBytes method from https://jira.spring.io/secure/attachment/11851/OracleLobHandler.java
try {
if (bytes != null) {
//prepareLob
BLOB blob = BLOB.createTemporary(preparedStatement.getConnection(), true, BLOB.DURATION_SESSION);
//callback.populateLob
OutputStream os = blob.getBinaryOutputStream();
try {
os.write(bytes);
} catch (Exception e) {
throw new SQLException(e);
} finally {
try {
os.close();
} catch (Exception e) {
e.printStackTrace();//ignore
}
}
preparedStatement.setBlob(i, blob);
} else {
preparedStatement.setBlob(i, (Blob) null);
}
} catch (Exception e) {
throw new SQLException(e);
}
}
/** see getBlobAsBytes method from https://jira.spring.io/secure/attachment/11851/OracleLobHandler.java */
private byte[] getBlobAsBytes(BLOB blob) throws SQLException {
//initializeResourcesBeforeRead
if(!blob.isTemporary()) {
blob.open(BLOB.MODE_READONLY);
}
//read
byte[] bytes = blob.getBytes(1L, (int)blob.length());
//releaseResourcesAfterRead
if(blob.isTemporary()) {
blob.freeTemporary();
} else if(blob.isOpen()) {
blob.close();
}
return bytes;
}
#Override
public byte[] getNullableResult(ResultSet resultSet, String columnName) throws SQLException {
try {
//use a custom oracle.sql.BLOB
BLOB blob = (BLOB) resultSet.getBlob(columnName);
return getBlobAsBytes(blob);
} catch (Exception e) {
throw new SQLException(e);
}
}
#Override
public byte[] getNullableResult(ResultSet resultSet, int i) throws SQLException {
try {
//use a custom oracle.sql.BLOB
BLOB blob = (BLOB) resultSet.getBlob(i);
return getBlobAsBytes(blob);
} catch (Exception e) {
throw new SQLException(e);
}
}
#Override
public byte[] getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
try {
//use a custom oracle.sql.BLOB
BLOB blob = (BLOB) callableStatement.getBlob(i);
return getBlobAsBytes(blob);
} catch (Exception e) {
throw new SQLException(e);
}
}
}
Add the type handlers package to mybatis configuration. As you can see, I am using spring-mybatis:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="typeHandlersPackage" value="package.where.customhandler.is" />
</bean>
And then, you can read byte[] from Oracle BLOBs from Mybatis:
public class Bean {
private byte[] file;
}
interface class Dao {
#Select("select file from some_table where id=#{id}")
Bean getBean(#Param("id") String id);
}
I hope this will help.
This is an adaptation of this excellent answer: This is an adaptation of this excellent answer: https://stackoverflow.com/a/27522590/2692914.

Spring integeration - error-channel handling issues

I am new in Spring Integeration.I have one requirement Using spring integeration
read a txt file (from Source folder)
do some validation
if validation is success -write into sucess file (in sucess folder)
If the validation is fail -write into failure file (in error folder)
if the file format is incorrect means I have to move that file into error folder(Ex excepted columns is 2 but in my file contain columns is 1)
My config file is like this
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:si="http://www.springframework.org/schema/integration"
xmlns:file="http://www.springframework.org/schema/integration/file"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration-1.0.xsd
http://www.springframework.org/schema/integration/file
http://www.springframework.org/schema/integration/file/spring-integration-file-1.0.xsd">
<bean id="checkCSVReader"
class="com.check.wrapper">
<property name="pzMapXML" value="classpath:sampleFileFormat.xml" />
</bean>
<bean id="checkTrasnFomer"
class="com.check.checkTransfomer">
<property name="wrapper" ref="checkCSVReader" />
</bean>
<bean id="fileErrorProcessor"
class="com.check.ErrorChannelWriter">
</bean>
<bean id="listToStringTrans"
class="com.check.ListToStringTransfomer"></bean>
<bean id="validation"
class="com.check.Validation"/>
<file:inbound-channel-adapter directory="file://D:\check\soruce" prevent-duplicates="false"
auto-create-directory="true" channel="readChannel" >
<si:poller id="Poller">
<si:interval-trigger interval="10000" />
</si:poller>
</file:inbound-channel-adapter>
<si:channel id="readChannel" />
<si:chain input-channel="readChannel" output-channel="processChannel">
<si:header-enricher error-channel="errorFile" />
<file:file-to-string-transformer />
<si:transformer ref="checkTrasnFomer" method="transform" />
<si:service-activator ref="validation"
method="validate" />
</si:chain>
<si:channel id="processChannel" />
<si:transformer ref="listToStringTrans" method="transformList"
input-channel="processChannel" output-channel="finalOut" />
<si:channel id="finalOut" />
<file:outbound-channel-adapter id="checkSuccFileOutBound"
auto-create-directory="true" delete-source-files="true"
directory="file://D:\check\success" channel="finalOut">
</file:outbound-channel-adapter>
<si:channel id="errorFile" />
<si:transformer ref="fileErrorProcessor"
input-channel="errorFile" output-channel="errorChannel" method="transformError" />
<file:outbound-channel-adapter id="errorChannel"
directory="file://D:\check\error" delete-source-files="true"
/>
<si:channel id="checkFileErr" />
</beans>
my checkFlatPackCVSParserWrapper class is
public class checkFlatPackCVSParserWrapper {
private static final Log LOG = LogFactory.getLog("checkFlatPackCVSParserWrapper");
private Resource pzMapXML;
private char delimiter = ',';
private char qualifier = '"';
private boolean ignoreFirstRecord = false;
public Resource getPzMapXML() {
return pzMapXML;
}
public void setPzMapXML(Resource pzMapXML) {
this.pzMapXML = pzMapXML;
}
public char getDelimiter() {
return delimiter;
}
public void setDelimiter(char delimiter) {
this.delimiter = delimiter;
}
public char getQualifier() {
return qualifier;
}
public void setQualifier(char qualifier) {
this.qualifier = qualifier;
}
public boolean isIgnoreFirstRecord() {
return ignoreFirstRecord;
}
public void setIgnoreFirstRecord(boolean ignoreFirstRecord) {
this.ignoreFirstRecord = ignoreFirstRecord;
}
public Parser getParser(String csv) {
if(LOG.isDebugEnabled())
LOG.debug("getParser: " + csv);
Parser result = null;
try {
result = DefaultParserFactory.getInstance().newDelimitedParser(
pzMapXML.getInputStream(), //xml column mapping
new ByteArrayInputStream(csv.getBytes()), //txt file to parse
delimiter, //delimiter
qualifier, //text qualfier
ignoreFirstRecord);
}catch (Exception e) {
if(LOG.isDebugEnabled())
LOG.debug("Unable to read file: " + e );
throw new RuntimeException("File Parse exception");
}
return result;
}
}
sampleFileFormat.xml is
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE PZMAP SYSTEM "flatpack.dtd" >
<PZMAP>
<COLUMN name="FIRSTNAME" />
<COLUMN name="LASTNAME" />
</PZMAP>
and checkTransfomer is
public class checkTransfomer {
private static final Log LOG = LogFactory.getLog(checkTransfomer.class);
private CheckFlatPackCVSParserWrapper wrapper;
public String transform(String csv) {
Parser parser = wrapper.getParser(csv);
if(LOG.isDebugEnabled()) {
LOG.debug("Parser is: " + parser);
}
DataSet ds = parser.parse();
ArrayList<Check> list = new ArrayList<Check>();
while(ds.next()) {
Check check= new Check();
check.setFirstName(ds.getString("FIRSTNAME"));
check.setLastName(ds.getString("LASTNAME"));
if(LOG.isDebugEnabled()) {
LOG.debug("Bean value is: " + bean);
}
list.add(bean);
}
if(LOG.isDebugEnabled()) {
LOG.debug("Records fetched is: " + list.size());
}
return list.toString();
}
public CheckFlatPackCVSParserWrapper getWrapper() {
return wrapper;
}
public void setWrapper(CheckFlatPackCVSParserWrapper wrapper) {
this.wrapper = wrapper;
}
And my ErrorChannelWriter is
public class ErrorChannelWriter {
public static final Log LOG = LogFactory.getLog(ErrorChannelWriter.class);
public Message<?> transformError(ErrorMessage errorMessage) {
if (LOG.isDebugEnabled()) {
LOG.debug("Transforming errorMessage is: " + errorMessage);
}
return ((MessagingException) errorMessage.getPayload())
.getFailedMessage();
}
}
and my validagtion class is
com.check.Validation
public class Validation
{
void validation(CheckCheck)
{
if(Check.getFirstName().equals("maya"))
{
throw new RuntimeException("Name Already exist");
}
}
}
and my ListToStringTransfomer is
public class ListToStringTransfomer {
private static final Log LOG=LogFactory.getLog(ListToStringTransfomer.class);
public String transformList(List<IssueAppBean> list) {
return list.toString();
}
}
and my file containing one fields instead of two fields
> maya
here my file format is wrong, so record is moving to error folder.but there is no error message. how can i add error message(TOO FEW COLUMNS WANTED: 2 GOT: 1) when my file format is incorrect.
my requirement is in my error file should contaion
maya -TOO FEW COLUMNS WANTED: 2 GOT: 1 or(Any error message )
please give me any solution
I don't think that you should go through the error channel to solve this requirement. The main reason for this is that invalid input in this case is an expected scenario. The errorChannel is the channel that Spring Integration sends messages to if an unexpected exception happens in an endpoint.
If you add some header to the message if a validation failed, you can route based on that header and also record the failure message there. You can then send your error message to a log file or whatever on your own.

Resources