Simple Example of Mybatis JAVA Maven Implementation 8 – Customized Type Handler

In the following example, we will show how to write a customized type handler. The type handler is a mybatis feature to allow you to intercept and update the information before the information is committed to the database, and/or after the information is retrieved from database but before it is being used by your application.

One example I am showing here, is that assume you maintain the user login information in database, and you want to encrypt the password before writing to the database and you want to decrypt it after retrieving from database.

1. create a user login table

delimiter $$

CREATE TABLE `user` (
  `user_id` int(11) NOT NULL AUTO_INCREMENT,
  `user_name` varchar(45) NOT NULL,
  `password` varchar(45) NOT NULL,
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1$$


2. create a user bean class

package com.springriver.example.mybatis.bean;

public class User {
	private int userId;
	private String userName;
	private String password;
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public int getUserId() {
		return userId;
	}
	public void setUserId(int userId) {
		this.userId = userId;
	}
	
}

3. create the mapper interfaces and the manager class
UserMapper.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="com.springriver.example.mybatis.mapper.UserMapper">


<select id="selectUserById" parameterType="int" resultType="User">
	select user_id as userId, user_name as userName, password from user where user_id = #{id}
</select>

<insert id="insertUser" parameterType="User" useGeneratedKeys="true" keyProperty="userId">
	insert into user(user_name, password) values (#{userName}, #{password})
</insert>



</mapper>

UserMapper.java

package com.springriver.example.mybatis.mapper;

import com.springriver.example.mybatis.bean.User;

public interface UserMapper {
	public User selectUserById(Integer id);
	public int insertUser(User user);
}

UserManager.java

package com.springriver.example.mybatis.util;

import com.springriver.example.mybatis.bean.User;
import com.springriver.example.mybatis.mapper.UserMapper;


public class UserManager {
	public static User selectUserById(final int id){
		return (User)new DatabaseOperation() {
			
			@Override
			public Object performAction() {
				// TODO Auto-generated method stub
				return executeMapperFunction(UserMapper.class, "selectUserById", id);
			}
		}.invoke();
	}
	
	public static int insertUser(final User user){
		return (Integer)new DatabaseOperation(true) {
			
			@Override
			public Object performAction() {
				// TODO Auto-generated method stub
				executeMapperFunction(UserMapper.class, "insertUser", user);
				return user.getUserId();
			}
		}.invoke();
	}
}

4. update the database-config.xml to include the new mapper.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>
	<typeAliases>
		<typeAlias type="com.springriver.example.mybatis.bean.Category" alias="Category"/>
		<typeAlias type="com.springriver.example.mybatis.bean.Product" alias="Product"/>	
		<typeAlias type="com.springriver.example.mybatis.bean.User" alias="User"/>	
	</typeAliases>
	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="${driver}" />
				<property name="url" value="${url}" />
				<property name="username" value="${username}" />
				<property name="password" value="${password}" />
			</dataSource>
		</environment>
	</environments>
	<mappers>
		<mapper resource="com/springriver/example/mybatis/mapper/CategoryMapper.xml" />
		<mapper resource="com/springriver/example/mybatis/mapper/ProductMapper.xml" />
		<mapper resource="com/springriver/example/mybatis/mapper/UserMapper.xml" />
	</mappers>
</configuration>

5. write a junit testing class to test the UserManager functions

package com.springriver.example.mybatis;

import org.junit.Test;

import com.springriver.example.mybatis.bean.User;
import com.springriver.example.mybatis.util.UserManager;

public class UserManagerTest {
	@Test
	public void testUser(){
		User user = new User();
		user.setUserName("jsmith");
		user.setPassword("donkey");
		int insertedUserId = UserManager.insertUser(user);
		User selectedUser = UserManager.selectUserById(insertedUserId);
		assert(selectedUser != null);
		assert(selectedUser.getUserName().equals(user.getUserName()));
	}
	
	
}

6. query the database table User to see the information you inserted.

result

you can see that the password is persisted to database as clear text.
Let’s write use type handler to encrypt the password before persisting to database and decrypt it after retrieving from database

7. write a util class to do AES encryption/decryption. refer to my tutorial on this topic.

package com.springriver.example.mybatis.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.springframework.core.io.ClassPathResource;

public class EncryptionUtil {
	private static SecretKeySpec skeySpec;
	
	static {	
		try {			
			ClassPathResource res = new ClassPathResource("key.key");
			if(res != null){
				File file = res.getFile();
				FileInputStream input = new FileInputStream(file);
				byte[] in = new byte[(int)file.length()];
				input.read(in);
				skeySpec = new SecretKeySpec(in, "AES");
				input.close();
			}
		}catch (FileNotFoundException e) {
			e.printStackTrace();
		}catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	
	public static byte[] encrypt(String input) 
			throws GeneralSecurityException, NoSuchPaddingException{
	       Cipher cipher = Cipher.getInstance("AES");

	       cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
	       return cipher.doFinal(input.getBytes());
		
	}
	
	
	public static String decrypt(byte[] input) throws GeneralSecurityException, NoSuchPaddingException{
		Cipher cipher = Cipher.getInstance("AES");
		cipher.init(Cipher.DECRYPT_MODE, skeySpec);
		return new String(cipher.doFinal(input));
	}
	
	

}

8. write a type handler

package com.springriver.example.mybatis.handler;

import java.security.GeneralSecurityException;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import javax.crypto.NoSuchPaddingException;

import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;

import com.springriver.example.mybatis.util.EncryptionUtil;

public class PasswordTypeHandler implements TypeHandler<String> {

	public String getResult(ResultSet arg0, String arg1) throws SQLException {
		// TODO Auto-generated method stub
		String retValue = null;
		try {
			retValue = EncryptionUtil.decrypt(arg0.getBytes(arg1));
		} catch (NoSuchPaddingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (GeneralSecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return retValue;
	}

	public String getResult(ResultSet arg0, int arg1) throws SQLException {
		String retValue = null;
		try {
			retValue = EncryptionUtil.decrypt(arg0.getBytes(arg1));
		} catch (NoSuchPaddingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (GeneralSecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return retValue;
	}

	public String getResult(CallableStatement arg0, int arg1) throws SQLException {
		String retValue = null;
		try {
			retValue = EncryptionUtil.decrypt(arg0.getBytes(arg1));
		} catch (NoSuchPaddingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (GeneralSecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return retValue;
	}

	public void setParameter(PreparedStatement arg0, int arg1, String arg2,
			JdbcType arg3) throws SQLException {
	byte[] password = null;
		
		if (arg2 != null)
			try {
				password = EncryptionUtil.encrypt(arg2) ;
			} catch (NoSuchPaddingException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (GeneralSecurityException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		arg0.setBytes(arg1, password);
		
	}

}

9. update UserMapper.xml to apply the new TypeHandler to password field

<?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="com.springriver.example.mybatis.mapper.UserMapper">


<select id="selectUserById" parameterType="int" resultMap="UserMap">
	select user_id as userId, user_name as userName, password from user where user_id = #{id}
</select>

<insert id="insertUser" parameterType="User" useGeneratedKeys="true" keyProperty="userId">
	insert into user(user_name, password) values (#{userName}, 
		#{password,jdbcType=BINARY,javaType=java.lang.String,
		typeHandler=com.springriver.example.mybatis.handler.PasswordTypeHandler})
</insert>

<resultMap type="User" id="UserMap">
<result property="password" column="password" javaType="java.lang.String" jdbcType="BINARY" 
	typeHandler="com.springriver.example.mybatis.handler.PasswordTypeHandler"/>

</resultMap>


</mapper>

10 run the UserManagerTest again, and query the user table

result-after-encryption

you can see the password is stored as encrypted text in database.

download the source
springriver.mybatis.simple8

Tagged with: , , ,
Posted in MyBatis

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>