mybatis orm解决方案
1 时区具体做法:
1.1
public class TimezoneTypeHandler extends BaseTypeHandler<Date> {
protected int dbZone;
public TimezoneTypeHandler(int dbZone) {
this.dbZone = dbZone;
}
@Override
public void setNonNullParameter(PreparedStatement preparedStatement, int i, Date dateJvm, JdbcType jdbcType) throws SQLException {
if(dateJvm != null) {
Date dateDb = TimezoneManager.timezoneJvmToDbWhenInsertAndUpdate(dateJvm, this.dbZone);
preparedStatement.setDate(i, new java.sql.Date(dateDb.getTime()));
}
}
@Override
public Date getNullableResult(ResultSet resultSet, String s) throws SQLException {
java.sql.Date dateDb = resultSet.getDate(s);
return getJvmDateByDbDate(dateDb);
}
【下省略这2个方法】
@Override
public Date getNullableResult(ResultSet resultSet, int i) throws SQLException {
java.sql.Date dateDb = resultSet.getDate(i);
return getJvmDateByDbDate(dateDb);
}
@Override
public Date getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
java.sql.Date dateDb = callableStatement.getDate(i);
return getJvmDateByDbDate(dateDb);
}
private Date getJvmDateByDbDate(java.sql.Date dateDb) {
if(dateDb == null) return null;
return TimezoneManager.timezoneDbToJvmWhenQuery(new Date(dateDb.getTime()), this.dbZone);
}
}
public class TimezoneNewYorkTypeHandler extends TimezoneTypeHandler {
private static final int DB_ZONE_NEW_YORK = -4;
public TimezoneNewYorkTypeHandler() {
super(DB_ZONE_NEW_YORK);
}
}
@Result(column = "LOAD_TIME", property = "loadTime", typeHandler = TimezoneNewYorkTypeHandler.class),
LOAD_TIME = #{attachmentDto.loadTime,typeHandler = com.xxx.config.typehandler.TimezoneNewYorkTypeHandler},
1.2 bug
1.2.1 重大bug,这里应用java.sql.Timestamp,而不是java.sql.Date,虽然用Date时有时也能取到时分秒
1.2.2 插入/更新时,对于输入为null的Date,jdbcType=TIMESTAMP没用,需要
@Override
public void setParameter(PreparedStatement ps, int i, Date 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.setDate(i, null);
} 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 {
try {
setNonNullParameter(ps, i, parameter, jdbcType);
} catch (Exception e) {
throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . " +
"Try setting a different JdbcType for this parameter or a different configuration property. " +
"Cause: " + e, e);
}
}
}
BaseTypeHandler中的setParameter方法:
ps.setNull(i, jdbcType.TYPE_CODE);
OTHER(1111),
怪不得老是报1111
1.2.3
setNonNullParameter
中,可以不做非null判断,因为看方法名就知道了,传进来的肯定是非null
2 orm具体做法:
RequestDto
long requestId
UserDto user
Set<CommunicationDto> setCommunication
Set<Attach> setAttach
CommunicationDto
ccommunicationId
UserDto User
requestId
Set<AttahcmentDto>
AttachmentDto
communicationId
request detailId userid ... commiunication communicationId userId requestid ... attach communicationId
requestId
request
detail 级联插入,更新request时,可插入detail、可更新detail,看detailid
set<communication> 级联插入,分离更新
user 插入request时,认为user永远已存在
Set<Attach>
communication
Set<attach> 级联插入,分离更新
user 插入communication时,认为user永远已存在
2.1 communication中查询user,插入/更新user
(request中查询user,request中查询detail)
2.1.1
@Result(column = "UPDATE_USER", property = "User", typeHandler = UserDtoOneOneTypeHandler.class),
"#{RequestCommunicationDto.User,typeHandler = com.xxx.config.typehandler.UserDtoOneOneTypeHandler},\n" +
public class UserDtoOneOneTypeHandler extends BaseTypeHandler<UserDto> {
@Override
public void setNonNullParameter(PreparedStatement preparedStatement, int i, UserDto UserDto, JdbcType jdbcType) throws SQLException {
if(UserDto != null) {
String id = UserDto.getUserId();
preparedStatement.setString(i, id);
}
}
@Override
public UserDto getNullableResult(ResultSet resultSet, String s) throws SQLException {
String id = resultSet.getString(s);
return getUserById(id);
}
private UserDto getUserById(String id) {
Injector injector = CRFGuiceContext.getInjector();
UserMapper userMapper = injector.getInstance(UserMapper.class);
UserDto user = userMapper.queryForObject(id);
return user;
}
}
2.1.2 bug
2.1.2.1 setPara时,加入communication中userDto为null,则在userId这个字段插入null,否则插入userId
可不做非null判断,同1.2.3
2.1.2.2 要考虑join字段db查出来为null情况:禁止用基本数据类型接收resultSet.getid,Integer-》int直接报错
private UserDto getUserById(String id) {
if(id == null || "".equals(id))
return null;
Injector injector = CRFGuiceContext.getInjector();
UserMapper userMapper = injector.getInstance(UserMapper.class);
UserDto user = userMapper.queryForObject(id);
return user;
}
2.1.2.3 要考虑插入/更新时,无关联对象mybatis报错
"#{RequestCommunicationDto.User,jdbcType=NUMERIC,typeHandler = com.xxx.config.typehandler.UserDtoOneOneTypeHandler},\n" +
否则报错:同1.2.2
Caused by: org.apache.ibatis.type.TypeException: Could not set parameters for mapping: ParameterMapping{property='RequestCommunicationDto.User', mode=IN, javaType=class java.lang.Object, jdbcType=null, numericScale=null, resultMapId='null', jdbcTypeName='null', expression='null'}. Cause: org.apache.ibatis.type.TypeException: Error setting null for parameter #3 with JdbcType OTHER . Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. Cause: java.sql.SQLException: Invalid column type: 1111
at org.apache.ibatis.scripting.defaults.DefaultParameterHandler.setParameters(DefaultParameterHandler.java:89)
at org.apache.ibatis.executor.statement.PreparedStatementHandler.parameterize(PreparedStatementHandler.java:85)
at org.apache.ibatis.executor.statement.RoutingStatementHandler.parameterize(RoutingStatementHandler.java:63)
at org.apache.ibatis.executor.SimpleExecutor.prepareStatement(SimpleExecutor.java:77)
at org.apache.ibatis.executor.SimpleExecutor.doUpdate(SimpleExecutor.java:48)
at org.apache.ibatis.executor.BaseExecutor.update(BaseExecutor.java:115)
at org.apache.ibatis.executor.CachingExecutor.update(CachingExecutor.java:75)
at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:170)
... 52 common frames omitted
Caused by: org.apache.ibatis.type.TypeException: Error setting null for parameter #3 with JdbcType OTHER . Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. Cause: java.sql.SQLException: Invalid column type: 1111
at org.apache.ibatis.type.BaseTypeHandler.setParameter(BaseTypeHandler.java:47)
at org.apache.ibatis.scripting.defaults.DefaultParameterHandler.setParameters(DefaultParameterHandler.java:87)
... 59 common frames omitted
Caused by: java.sql.SQLException: Invalid column type: 1111
2.2 communication中查询Set<Attach>
(Request中查询Set<Communication>,Request中查询Set<Attach>
2.2.1
@Result(column = "_UPDATE_ID", property = "UpdateId"),
@Result(column = "_UPDATE_ID", property = "attachment", typeHandler = AttachmentInCommunicationOneManyTypeHandler.class),
updateid===communicationId,主键建2个Result,一个主键field,一个利用communication主键查询attach表,set<Attach>
public class AttachmentInCommunicationOneManyTypeHandler extends AbstractAttachmentOneManyTypeHandler {
@Override
protected Set<AttachmentDto> getAttachment(Long communicationId) {
Injector injector = CRFGuiceContext.getInjector();
AttachmentMapper attachmentMapper = injector.getInstance(AttachmentMapper.class);
Set<AttachmentDto> set = attachmentMapper.queryForSetByCommunicationId(communicationId);
return set;
}
}
因为request中也有Set<Attach>,getAttachment拉出来,对Commnication就是根据CommunicationId查询Set,对Request就是根据RequestId查询Set
abstract public class AbstractAttachmentOneManyTypeHandler extends BaseTypeHandler<Set<AttachmentDto>> {
@Override
public void setNonNullParameter(PreparedStatement preparedStatement, int i, Set<AttachmentDto> attachmentDtoSet, JdbcType jdbcType) throws SQLException {
// TODO: 8/2/2020 insert set attach when insert communication【but not here】
}
@Override
public Set<AttachmentDto> getNullableResult(ResultSet resultSet, String s) throws SQLException {
Long communicationId = resultSet.getLong(s);
return getAttachment(communicationId);
}
abstract protected Set<AttachmentDto> getAttachment(Long communicationId);
}
2.2.2 bug
2.2.2.1
select输入非空判断:虽然输入的是主键
@Override
protected Set<AttachmentDto> getAttachment(Long communicationId) {
if(communicationId == null)
return Collections.emptySet();
Injector injector = CRFGuiceContext.getInjector();
AttachmentMapper attachmentMapper = injector.getInstance(AttachmentMapper.class);
Set<AttachmentDto> set = attachmentMapper.queryForSetByCommunicationId(communicationId);
return set;
}
**********************************
2.3 插入request,插入detail
先插入/更新(取决于detail有无id)detail,取得sequence,set request detailid,插入request
2.3.1 插入,插入detail,忽视已存在的detail
"REQUEST_ID,\n" +
"_DETAILS_ID,\n" +
"#{RequestDto,typeHandler = com.xxx.config.typehandler.RequestInsertTypeHandler},\n" +
"<if test=\"RequestDto.Details != null \">#{RequestDto.Details.DetailsId, jdbcType=NUMERIC},</if>\n" +
"<if test=\"RequestDto.Details == null \">null,</if>\n" +
public class RequestInsertTypeHandler extends BaseTypeHandler<RequestDto> {
@Override
public void setNonNullParameter(PreparedStatement preparedStatement, int i, RequestDto requestDto, JdbcType jdbcType) throws SQLException {
Injector injector = CRFGuiceContext.getInjector();
DetailsDto detailsDto = requestDto.getDetails();
DetailsCUDMapper detailsCUDMapper = injector.getInstance(DetailsCUDMapper.class);
if(detailsDto != null && detailsDto.getDetailsId() == null) {
detailsCUDMapper.insert(detailsDto);
}
// when insert request, if request.detail has id, do nothing
if(detailsDto != null && detailsDto.getDetailsId() != null) {
;
}
2.3.2 更新request,插入/更新detail
"REQUEST_ID = #{RequestDto, typeHandler = com.xxx.config.typehandler.RequestUpdateTypeHandler}, \n" +
"<if test=\"RequestDto.Details != null \">_DETAILS_ID = #{RequestDto.Details.DetailsId, jdbcType=NUMERIC},</if>" +
"<if test=\"RequestDto.Details == null \">null,</if>\n" +
public class RequestUpdateTypeHandler extends BaseTypeHandler<RequestDto> {
@Override
public void setNonNullParameter(PreparedStatement preparedStatement, int i, RequestDto requestDto, JdbcType jdbcType) throws SQLException {
Injector injector = CRFGuiceContext.getInjector();
DetailsDto detailsDto = requestDto.getDetails();
DetailsCUDMapper detailsCUDMapper = injector.getInstance(DetailsCUDMapper.class);
if(detailsDto != null && detailsDto.getDetailsId() == null) {
detailsCUDMapper.insert(detailsDto);
}
if(detailsDto != null && detailsDto.getDetailsId() != null) {
detailsCUDMapper.update(detailsDto);
}
2.4 插入communication时,插入Set<Attach>
(插入request,插入Set<Communication>,Set<Attach>)
2.4.1
取得request sequence,set communication中requestId,插入set<communication>,插入request
"_UPDATE_ID,\n" + "UPDATE_TIME,\n" + "UPDATE_USER,\n" +
"#{RequestCommunicationDto,typeHandler = com.xxx.config.typehandler.CommunicationInsertTypeHandler},\n" +
"#{RequestCommunicationDto.updateTime,typeHandler = com.xxx.config.typehandler.TimezoneNewYorkTypeHandler},\n" +
"#{RequestCommunicationDto.User,typeHandler = com.xxx.config.typehandler.UserDtoOneOneTypeHandler},\n" +【要 jdbcType=NUMERIC】
务必注意末字段尾部不能有逗号
CommunicationInsertTypeHandler.setNonNullParameter 解决Set<Attach>
TimezoneTypeHandler.setNonNullParameter 解决插入时间时区
UserDtoOneOneTypeHandler.setNonNullParameter 解决Communication下UserDto对象取出主键,db插入,若没有,为null,则走jdbcType=NUMERIC
public class CommunicationInsertTypeHandler extends BaseTypeHandler<RequestCommunicationDto> {
@Override
public void setNonNullParameter(PreparedStatement preparedStatement, int i, RequestCommunicationDto communicationDto, JdbcType jdbcType) throws SQLException {
String sequenceName = communicationDto.getSequenceName();
Injector injector = CRFGuiceContext.getInjector();
SequenceMapper sequenceMapper = injector.getInstance(SequenceMapper.class);
AttachmentMapper attachmentMapper = injector.getInstance(AttachmentMapper.class);
Long communicationId = sequenceMapper.queryForSequence(sequenceName);
communicationDto.setUpdateId(communicationId);
Set<AttachmentDto> set = communicationDto.getAttachment();
if(set != null && set.size() > 0) {
for(AttachmentDto attachmentDto : set) {
attachmentDto.setCommunicationId(communicationId);
attachmentMapper.insert(attachmentDto);
}
}
preparedStatement.setLong(i, communicationId);
}
取得sequence
主键回写
Set<Attach> 插入communicationid, insert
将主键给到mybatis VALUE
2.4.2 改进:
"#{RequestCommunicationDto,typeHandler = com.xxx.config.typehandler.CommunicationInsertTypeHandler},\n" +
"#{RequestCommunicationDto.updateTime,typeHandler = com.xxx.config.typehandler.TimezoneNewYorkTypeHandler},\n" +
"#{RequestCommunicationDto.User,jdbcType=NUMERIC,typeHandler = com.xxx.config.typehandler.UserDtoOneOneTypeHandler},\n" +
"#{RequestCommunicationDto.comments,jdbcType=VARCHAR},\n" +
"#{RequestCommunicationDto.requestId,jdbcType=NUMERIC},\n" +
"#{RequestCommunicationDto.reconFlag,jdbcType=VARCHAR}\n" +
")";
@SelectKey(statement= "SELECT req.nextval FROM DUAL", before = true, keyProperty = "RequestCommunicationDto.UpdateId", resultType = Long.class)
@Insert(INSERT)
int insert(@Param("RequestCommunicationDto") RequestCommunicationDto RequestCommunicationDto);
public class CommunicationInsertTypeHandler extends BaseTypeHandler<RequestCommunicationDto> {
@Override
public void setNonNullParameter(PreparedStatement preparedStatement, int i, RequestCommunicationDto communicationDto, JdbcType jdbcType) throws SQLException {
Injector injector = CRFGuiceContext.getInjector();
AttachmentMapper attachmentMapper = injector.getInstance(AttachmentMapper.class);
Set<AttachmentDto> set = communicationDto.getAttachment();
if(set != null && set.size() > 0) {
for(AttachmentDto attachmentDto : set) {
attachmentDto.setCommunicationId(communicationDto.getUpdateId());
attachmentMapper.insert(attachmentDto);
}
}
preparedStatement.setLong(i, communicationDto.getUpdateId());
}
获取sequence与主键回写托管给MyBatis
3 改进
3.1 对2.1 2.2的改进
2.1.1查询
String QUERY_FOR_OBJECT = "SELECT UPDATE_USER,\n" + 之前就是因为要查的字段没在select里面导致使用one失败
@Result(column = "UPDATE_USER", property = "User",
one = @One(select="com.xxx.config.module.ods.riskods..UserMapper.queryForObject")),
2.1.1 插入
"<if test=\"RequestCommunicationDto.User != null \">#{RequestCommunicationDto.User.userId, jdbcType=NUMERIC},</if>\n" +
"<if test=\"RequestCommunicationDto.User == null \">null,</if>\n" +
此处还是要jdbcType=,防止userId为null,User不为null
直接给null居然可以
2.2 查询
@Result(column = "_UPDATE_ID", property = "attachment", javaType = Set.class,
many = @Many(select="com.cxxx.config.module.ods.riskods..AttachmentMapper.queryForSetByCommunicationId")),
4 小结:
1)插入,many_to_one 不处理 one_to_one 插入,one_to_many插入,many_to_many不处理
2)更新,many_to_one 不处理 one_to_one 插入or更新,one_to_many不处理,many_to_many不处理
3)删除,不处理
4)查询,4个注解都处理
浙公网安备 33010602011771号