编写实体类时注意的事项
@Table(name = "tb_spec_param")
@Data
public class SpecParam {
@Id
@KeySql(useGeneratedKeys = true)
private Long id;
private Long cid;
private Long groupId;
private String name;
private Boolean isNumeric;
private String unit;
private Boolean generic;
private Boolean searching;
@ColumnType(
column = "segments",
jdbcType = JdbcType.VARCHAR,
typeHandler = StringArrayHandler.class
)
private String[] segments;
}
对于上面实体类的isNumeric
,generic
,searching
这三个字段,可以看出共同点它们都是Boolean
类型,但是如果你写成boolean
类型的话,不管你数据库中值是什么,都会返回false。
同样对于int
类型的字段,也只能写成Interger
。其他字段同理,只能使用引用数据类型,不能使用基本数据类型。
参考文章:https://elim.iteye.com/blog/1847854
我们知道Java有java的数据类型,数据库有数据库的数据类型,在Java和数据库类型映射关系之间肯定有一个转换关系。在Mybatis中我们可以定义一个叫做TypeHandler类型处理器的东西,通过它可以实现Java类型跟数据库类型相互转换。
TypeHandler接口
public interface TypeHandler<T> {
/**
* 用于定义在Mybatis设置参数时该如何把Java类型的参数转换为对应的数据库类型
* @param ps 当前的PreparedStatement对象
* @param i 当前参数的位置
* @param parameter 当前参数的Java对象
* @param jdbcType 当前参数的数据库类型
* @throws SQLException
*/
void setParameter(PreparedStatement ps, int i, T parameter,
JdbcType jdbcType) throws SQLException;
/**
* 用于在Mybatis获取数据结果集时如何把数据库类型转换为对应的Java类型
* @param rs 当前的结果集
* @param columnName 当前的字段名称
* @return 转换后的Java对象
* @throws SQLException
*/
T getResult(ResultSet rs, String columnName) throws SQLException;
/**
* 用于在Mybatis通过字段位置获取字段数据时把数据库类型转换为对应的Java类型
* @param rs 当前的结果集
* @param columnIndex 当前字段的位置
* @return 转换后的Java对象
* @throws SQLException
*/
T getResult(ResultSet rs, int columnIndex) throws SQLException;
/**
* 用于Mybatis在调用存储过程后把数据库类型的数据转换为对应的Java类型
* @param cs 当前的CallableStatement执行后的CallableStatement
* @param columnIndex 当前输出参数的位置
* @return
* @throws SQLException
*/
T getResult(CallableStatement cs, int columnIndex) throws SQLException;
}
如果是自己写sql,其实挺容易的,自定义类型处理器实现TypeHandler
就好了,也可以继承这个接口的抽象类BaseTypeHandler
public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {
protected Configuration configuration;
public void setConfiguration(Configuration c) {
this.configuration = c;
}
public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
if (parameter == null) {
if (jdbcType == null) {
throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
}
try {
ps.setNull(i, jdbcType.TYPE_CODE);
} catch (SQLException e) {
throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
"Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. " +
"Cause: " + e, e);
}
} else {
setNonNullParameter(ps, i, parameter, jdbcType);
}
}
public T getResult(ResultSet rs, String columnName) throws SQLException {
T result = getNullableResult(rs, columnName);
if (rs.wasNull()) {
return null;
} else {
return result;
}
}
public T getResult(ResultSet rs, int columnIndex) throws SQLException {
T result = getNullableResult(rs, columnIndex);
if (rs.wasNull()) {
return null;
} else {
return result;
}
}
public T getResult(CallableStatement cs, int columnIndex) throws SQLException {
T result = getNullableResult(cs, columnIndex);
if (cs.wasNull()) {
return null;
} else {
return result;
}
}
public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException;
public abstract T getNullableResult(ResultSet rs, int columnIndex) throws SQLException;
public abstract T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException;
}
BaseTypeHandler
对TypeHandler
接口的四个方法做了一个简单的选择,把null值的情况都做了一个过滤。核心的取值和设值的方法还是抽象出来供子类实现,使用BaseTypeHandler
还有一个好处是它继承了另一个叫做TypeRererence
的抽象类,通过TypeRererence
的getRawType()方法可以获取到当前TypeHandler
所使用泛型的原始类型。这对Mybatis在注册TypeHandler的时候是非常有好处的,在没有指定javaType的情况下,Mybatis在注册TypeHandler时可以通过它来获取当前TypeHandler
所使用泛型的原始类型作为要注册的TypeHandler的javaType类型。
字符串和字符数组的转换
** 第一步:自定义一个类型处理器StringArrayHandler**
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class StringArrayHandler extends BaseTypeHandler<String[]> {
@Override
public String[] getNullableResult(ResultSet rs, String columnName)
throws SQLException {
return getStringArray(rs.getString(columnName));
}
@Override
public String[] getNullableResult(ResultSet rs, int columnIndex)
throws SQLException {
return this.getStringArray(rs.getString(columnIndex));
}
@Override
public String[] getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
return this.getStringArray(cs.getString(columnIndex));
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i,
String[] parameter, JdbcType jdbcType) throws SQLException {
//由于BaseTypeHandler中已经把parameter为null的情况做了处理,所以这里我们就不用再判断parameter是否为空了,直接用就可以了
StringBuffer result = new StringBuffer();
for (String value : parameter)
result.append(value).append(",");
result.deleteCharAt(result.length()-1);
ps.setString(i, result.toString());
}
private String[] getStringArray(String columnValue) {
if (columnValue == null)
return null;
return columnValue.split(",");
}
}
第二步:注册自定义的TypeHandler
建立了自己的TypeHandler之后就需要把它注册到Mybatis配置文件中,让Mybatis能够识别并使用它。注册TypeHandler主要有两种方式:
typeHandlers
元素的子元素typeHandler
来注册;typeHandlers
元素的子元素package
来注册。使用typeHandler
子元素注册时只能注册一个typeHandler,而使用功能package子元素注册时,Mybatis会把指定包里面的所有typeHandler都注册了。
使用typeHandler子元素注册时,我们需要通过它的handler属性来指明当前要注册的typeHandler的全名称。另外还需要两个附加属性javaType
用来指定java类型,和jdbcType
用来指定对应的jdbc;类型。
使用package子元素注册时需要我们通过它的name属性来指定要扫描的包,如果这时候我们也需要指定对应TypeHandler的javaType和jdbcType的话就需要我们在自定义的TypeHandler类上使用注解来定义了。
1. 如果我们指定了javaType和jdbcType,那么Mybatis会注册一个对应javaType和jdbcType的TypeHandler
2. 如果我们只指定了javaType属性,这时候又分两种情况:
@MappedJdbcTypes
指定了对应的jdbcType,那么Mybatis会一一注册指定的javaType、jdbcType和TypeHandler组合。 @MappedJdbcTypes({JdbcType.VARCHAR})
public class StringArrayTypeHandler implements TypeHandler<String[]> {
//..中间的实现代码省略了
//..
}
然后在mybatis配置文件中这样注册
<typeHandlers>
<typeHandler handler="com.tiantian.mybatis.handler.StringArrayTypeHandler" javaType="[Ljava.lang.String;"/>
typeHandlers>
则Mybatis在实际注册时候是以javaType为String数组,jdbcType为VARCHAR来注册StringArrayTypeHandler的。
@MaooedJdbcTypes
注解指定对应的jdbcType,那么这时候Mybatis会把jdbcType置为null,然后注册一个javaType、null、TypeHandler的组合。3. 既没有指定javaType属性,有没有指定jdbcType属性,或者只指定了jdbcType属性,又分三种情况
@MappedTypes
指定了对应的javaType,那么Mybatis将一一利用对应javaType和TypeHandler去以2的方式进行注册。 @MappedTypes({String[].class})
@MappedJdbcTypes({JdbcType.VARCHAR})
public class StringArrayTypeHandler implements TypeHandler<String[]> {
}
然后在Mybatis的配置文件中注册它时既不指定它的javaType属性,也不指定jdbcType属性
<typeHandlers>
<typeHandler handler="com.tiantian.mybatis.handler.StringArrayTypeHandler"/>
typeHandlers>
TypeHandler类上没有使用@MappedTypes指定对应的javaType时,如果当前的TypeHandler继承了TypeReference抽象类,Mybatis会利用TypeReference的getRawType()方法取到当前TypeHandler泛型对应的javaType类型,然后利用取得的javaType和TypeHandler以2的方式进行注册,同时还包括一个javaType为null以方式2进行的注册。TypeReference是Mybatis中定义的一个抽象类,主要是用来获取对应的泛型类型。
TypeHandler类上既没有标注@MappedTypes,又没有继承TypeReference抽象类。这种情况Mybatis会以null和null的组合注册该TypeHandler。
使用package子元素注册的TypeHandler会以上面的方式3进行注册。
<configuration>
<properties resource="config/jdbc.properties">properties>
<typeAliases>
<package name="com.tiantian.mybatis.model"/>
typeAliases>
<typeHandlers>
<typeHandler handler="com.tiantian.mybatis.handler.StringArrayTypeHandler" javaType="[Ljava.lang.String;" jdbcType="VARCHAR"/>
typeHandlers>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
dataSource>
environment>
environments>
<mappers>
<mapper resource="com/tiantian/mybatis/mapper/UserMapper.xml"/>
<package name="com.tiantian.mybatis.mapperinterface"/>
mappers>
configuration>
然后在mapperxml文件中定义resultMap
映射
<resultMap id="result" type="com.leyou.item.pojo.SpecParam">
<id column="id" property="id"/>
<result column="segments" property="segments" javaType="[Ljava.lang.String;"
jdbcType="VARCHAR" />
resultMap>
如果使用的是通用Mapper,即没有Mybatis配置文件和xml接口文件
通用Mapper提供了一个@ColumnType
注解来指定类型转换器
@ColumnType(
column = "segments", //字段名
jdbcType = JdbcType.VARCHAR, //jdbc类型,即数据库中的字段类型
typeHandler = StringArrayHandler.class //指定类型转换器
)
private String[] segments;
其它常用自定义类型转换器
参考文章:https://www.cnblogs.com/myadmin/p/5064274.html
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import com.winturn.utils.CommonJsonUtil;
/**
* Class: ArrayLongTypeHandler.java
* Description: 存储到数据库, 将LONG数组转换成字符串;
* 从数据库获取数据, 将字符串转为LONG数组.
*/
public class ArrayLongTypeHandler extends BaseTypeHandler<Object> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i,
Object parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, CommonJsonUtil.stringify(parameter));
}
@Override
public Object getNullableResult(ResultSet rs, String columnName)
throws SQLException {
return CommonJsonUtil.parse3(rs.getString(columnName), Object.class);
}
@Override
public Object getNullableResult(ResultSet rs, int columnIndex)
throws SQLException {
return CommonJsonUtil.parse3(rs.getString(columnIndex), Object.class);
}
@Override
public Object getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
return CommonJsonUtil.parse3(cs.getString(columnIndex), Object.class);
}
}
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import com.winturn.utils.CommonJsonUtil;
/**
* Class: ArrayStringTypeHandler.java
* Description: 存储到数据库, 将基本数据数组转换成字符串;
* 从数据库获取数据, 将字符串根据','拆分,转为数组.
*
*
*/
public class ArrayStringTypeHandler extends BaseTypeHandler<Object> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Object parameter,
JdbcType jdbcType) throws SQLException {
ps.setString(i, CommonJsonUtil.stringify(parameter));
}
@Override
public Object getNullableResult(ResultSet rs, String columnName)
throws SQLException {
return CommonJsonUtil.parse2(rs.getString(columnName), Object.class);
}
@Override
public Object getNullableResult(ResultSet rs, int columnIndex)
throws SQLException {
return CommonJsonUtil.parse2(rs.getString(columnIndex), Object.class);
}
@Override
public Object getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
return CommonJsonUtil.parse2(cs.getString(columnIndex), Object.class);
}
}
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import com.winturn.utils.CommonJsonUtil;
/**
* Class: ArrayIntegerTypeHandler.java
* Description: jsonarray 格式的字符串转换为相应的数组
*
*/
public class JsonArrayTypeHandler extends BaseTypeHandler<Object> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i,
Object parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, CommonJsonUtil.stringify(parameter));
}
@Override
public Object getNullableResult(ResultSet rs, String columnName)
throws SQLException {
return CommonJsonUtil.parseJsonToArray(rs.getString(columnName), Object.class);
}
@Override
public Object getNullableResult(ResultSet rs, int columnIndex)
throws SQLException {
return CommonJsonUtil.parseJsonToArray(rs.getString(columnIndex), Object.class);
}
@Override
public Object getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
return CommonJsonUtil.parseJsonToArray(cs.getString(columnIndex), Object.class);
}
}
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import com.winturn.utils.CommonJsonUtil;
/**
* Filename:JsonFloatTypeHandler.java
* Description: 将float类型数组装换成字符串
*
*/
public class JsonFloatTypeHandler extends BaseTypeHandler<Object> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i,
Object parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, CommonJsonUtil.stringifyObject(parameter));
}
@Override
public Object getNullableResult(ResultSet rs, String columnName)
throws SQLException {
return CommonJsonUtil.parseJsonToFloat(rs.getString(columnName), Object.class);
}
@Override
public Object getNullableResult(ResultSet rs, int columnIndex)
throws SQLException {
return CommonJsonUtil.parseJsonToFloat(rs.getString(columnIndex), Object.class);
}
@Override
public Object getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
return CommonJsonUtil.parseJsonToFloat(cs.getString(columnIndex), Object.class);
}
}
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Map;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.codehaus.jackson.map.ObjectMapper;
import com.winturn.exceptions.RolerServiceException;
import com.winturn.utils.JsonMapUtil;
/**
*
* @ClassName: JsonMapTypeHandler
* @Description: 将map装换成数组存储数据库,取出时将字符串装换成map
* @author sgl
* @date 2015年12月21日 下午6:22:50
*/
public class JsonMapTypeHandler extends BaseTypeHandler<Map<String, Object>> {
ObjectMapper mapper = new ObjectMapper();
@Override
public Map<String, Object> getNullableResult(ResultSet rs, String columnName) {
try {
String value = rs.getString(columnName);
return mapper.readValue(value, Map.class);
} catch (Exception e) {
}
return null;
}
@Override
public Map<String, Object> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
try {
String value = rs.getString(columnIndex);
return mapper.readValue(value, Map.class);
} catch (Exception e) {
}
return null;
}
@Override
public Map<String, Object> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
try {
String value = cs.getString(columnIndex);
return mapper.readValue(value, Map.class);
} catch (Exception e) {
}
return null;
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Map<String, Object> parameter, JdbcType jdbcType)
throws SQLException {
if (parameter == null) {
ps.setNull(i, Types.VARCHAR);
} else {
try {
ps.setString(i, JsonMapUtil.getJsonStrByMap(parameter));
} catch (RolerServiceException e) {
e.printStackTrace();
}
}
}
}
Mybatis中自动注册TypeHandler
register(Boolean.class, new BooleanTypeHandler());
register(boolean.class, new BooleanTypeHandler());
register(Byte.class, new ByteTypeHandler());
register(byte.class, new ByteTypeHandler());
register(Short.class, new ShortTypeHandler());
register(short.class, new ShortTypeHandler());
register(Integer.class, new IntegerTypeHandler());
register(int.class, new IntegerTypeHandler());
register(Long.class, new LongTypeHandler());
register(long.class, new LongTypeHandler());
register(Float.class, new FloatTypeHandler());
register(float.class, new FloatTypeHandler());
register(Double.class, new DoubleTypeHandler());
register(double.class, new DoubleTypeHandler());
register(String.class, new StringTypeHandler());
register(String.class, JdbcType.CHAR, new StringTypeHandler());
register(String.class, JdbcType.CLOB, new ClobTypeHandler());
register(String.class, JdbcType.VARCHAR, new StringTypeHandler());
register(String.class, JdbcType.LONGVARCHAR, new ClobTypeHandler());
register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler());
register(String.class, JdbcType.NCHAR, new NStringTypeHandler());
register(String.class, JdbcType.NCLOB, new NClobTypeHandler());
register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler());
register(BigInteger.class, new BigIntegerTypeHandler());
register(BigDecimal.class, new BigDecimalTypeHandler());
register(Byte[].class, new ByteObjectArrayTypeHandler());
register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler());
register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler());
register(byte[].class, new ByteArrayTypeHandler());
register(byte[].class, JdbcType.BLOB, new BlobTypeHandler());
register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler());
register(Object.class, UNKNOWN_TYPE_HANDLER);
register(Object.class, JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);
register(Date.class, new DateTypeHandler());
register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler());
register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler());
register(java.sql.Date.class, new SqlDateTypeHandler());
register(java.sql.Time.class, new SqlTimeTypeHandler());
register(java.sql.Timestamp.class, new SqlTimestampTypeHandler());
register(Character.class, new CharacterTypeHandler());
register(char.class, new CharacterTypeHandler());