Jfinal 有类似mybatis 的TypeHandler的自定义类型转换么?


⑴总会有一些特殊的数据,例如数组,坐标,几何,向量,经纬度,数据等类型。这些数据如果用字符串表示,那或多或少在java里会有操作不便,因此会封装成专门的自定义实体类型处理。

上面所说的数据,可以转成特殊格式的字符串,来存放到数据库的varchar类型的字段里。有一个问题,即使在数据库里用存用varchar存储⑴所说的数据,但在java编码使用,不一定都会用字符串,会用特定专门或自定义的实体类型来处理⑴所说的数据,这要怎么实现查询或插入时,调用自己编写的类型转换类呢?比如数据库里某个varchar存储坐标数据,字符串格式是 (x,y); 但在java里面,用带有x和y属性名的类来处理该类数据,例如point,更新到数据库,把point转成(x,y)格式的字符串,查询获得结果集时,把(x,y)转成point

⑶但还有另一个情况,不是所有的程序员都一定想用varchar类型来存这些数据,会用数据库专门处理的字段类型来存放这些类型的数据,这是因为数据库的select查询。不同的数据库有一些各自不同的专门存储该特定数据的字段类型,附带特定的查询函数来处理,比较,另外其数据库提jdbc驱动包里也有处理的实体。例如Postgresql的Polygon类型(多边形),在java这边驱动包里会提供PGpolygon,来提供java对数据库该类字段的获取和写入。

⑷ 基于⑶所说的情况,又有一个问题,有时候jdbc提供对特定字段的处理实体类型,一些程序员也不一定喜欢使用,总想自定义类型转换处理,例如postgresql的坐标字段在java里用jdbc驱动包提供的PGpoint字段进行插入和处理,但我觉得PGpoint不好用,我想用自己定义的point类处理,在java通过jdbc获得查询出结果集的时候,想把PGpoint转成我自己定义或扩展的point类,更新到数据库的时候,也是把我自己定义的point类转成PGpoint类


所以Jfinal 有类似mybatis 的TypeHandler的自定义类型转换处理么?或者有实现方法么?我不想在每次编写查询或插入代码的时候,都要编写一个手动调用转换的代码。我希望是转换方法要么写的表名映射的实体类里,要么写到专门的类型转换处理类,在使用jdbc时,根据“处理的实体类型”或“注解”调用相应的转换方法。




以下是我自己用mybatis的时候,处理多边形数据而编写的 类型转换处理类,用于PGpolygon与Points2D于转换的,查询postgresql数据库的polygon字段,java访问用PGpolygon字段处理,但我觉得不好用,我自己编写了一个Points2D,在访问数据库的时候,在jdbc那里与PGpolygon类型进行转换后,再访问到数据库里。代码如下

@MappedJdbcTypes({JdbcType.OTHER})

@MappedTypes({Points2D.class})
public class PGpolygonHandler extends BaseTypeHandler<Points2D>  {

        //更新插入处理
	@Override
	public void setNonNullParameter(PreparedStatement ps, int index, Points2D parameter, JdbcType jdbcType)
			throws SQLException {
		// TODO Auto-generated method stub
		StringBuffer sb=new StringBuffer();
		sb.append("'(");
		sb.append("(");
		sb.append(parameter.getList().get(0).x);
		sb.append(",");
		sb.append(parameter.getList().get(0).y);
		sb.append(")");
		
		for(int i=1;i<parameter.getList().size();i++) {
			sb.append(",");
			sb.append("(");
			sb.append(parameter.getList().get(i).x);
			sb.append(",");
			sb.append(parameter.getList());
			sb.append(")");
			
		}
		sb.append(")'");
		PGpolygon pg=new PGpolygon();
		pg.points=new PGpoint[parameter.getList().size()];
		for(int i=0;i<parameter.getList().size();i++) {
			Point2D.Double t=parameter.getList().get(i);
			pg.points[i]=new PGpoint(t.x,t.y);
			
		}
		
		
		  ps.setObject(index, pg);
	}

	//查询处理
	@Override
	public Points2D getNullableResult(ResultSet rs, String columnName) throws SQLException {
		// TODO Auto-generated method stub
		 
		
		PGpolygon pg=(PGpolygon) rs.getObject(columnName);
		if(pg==null||pg.points.length==0) {
				return new Points2D();
		}else {
			
			
			return new Points2D(pg);
			
			
		}
				
	
		
		
			
		
		
	
	}

	@Override
	public Points2D getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
		// TODO Auto-generated method stub
		
	
		
		PGpolygon pg=(PGpolygon) rs.getObject(columnIndex);
		if(pg==null||pg.points.length==0) {
				return new Points2D();
		}else {
			return new Points2D(pg);
		}
	}

	@Override
	public Points2D getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
		// TODO Auto-generated method stub
		
		
		PGpolygon pg=(PGpolygon) cs.getObject(columnIndex);
		if(pg==null||pg.points.length==0) {
				return new Points2D();
		}else {
			return new Points2D(pg);
		}
	}

	
	

}



评论区

JFinal

2020-04-06 16:57

扩展 ModelBuilder、RecordBuilder 就可以了,扩展完以后,通过 Dialect.setModelBuilder/setRecordBulder 方法就可以切换过去了

tctc4869

2020-04-06 20:40

@JFinal 我还是不理解,请问一下,要创建一个ModelBuilder、RecordBuilder的实例。设置到 DruidPlugin或ActiveRecordPlugin里?用什么方法设置?

tctc4869

2020-04-06 20:41

或者有实例代码?

JFinal

2020-04-06 22:10

@tctc4869 扩展这两个组件的实例代码也是现成的:
https://gitee.com/jfinal/jfinal/tree/master/src/main/java/com/jfinal/plugin/activerecord/builder

有两个 ModelBuilder、RecordBuilder 扩展,参考着写即可,假定你扩展出来的叫 MyModelBuilder.java、MyRecordBuilder.java 配置方法如下:
ActiveRecordPlugin arp = new ActiveRecordPlugin(...);
Dialect dialect = new MySqlDialect();

// 下面两行是关键
dialect.setModelBuilder(new MyModelBuilder());
dialect.setRecordBuilder(new MyRecordBuilder());

arp.setDialect(dialect);
me.add(arp);

zzutligang

2020-04-07 11:07

这个厉害!

zhangtianxiao

2020-04-07 14:52

ModelBuilder 和 RecordBuilder 决定了如何解析resultSet, 包括java中的primitive type,string, 以及PgObject的子类, PgObject#getValue返回值是string, 而psql本身是有类型转换操作符的, 扩展方言折腾PgObject或许得不偿失, 请

tctc4869

2020-04-07 15:07

@zhangtianxiao 扩展方言折腾是因为Json化的问题,psql本身拥有的类型转换操作符,可能不一定满足json序列化的需要

tctc4869

2020-04-07 16:32

@JFinal 为此我看了一下RecordBuilder源码,只有buid,和buildLabelNamesAndTypes,build有一个ResultSet类型的参数,有这个,就代表是获取结果集的应该是,那保存方法是哪个什么? jdbc更新数据到数据库用的是PreparedStatement,可是我在RecordBuilder里看了一下,没看到有PreparedStatement类型参数的方法

tctc4869

2020-04-07 16:33

数据插入,更新,删除,用的是哪个类哪个方法?

JFinal

2020-04-07 16:37

@tctc4869 只需关注从数据库取数据, 更新、写入数据不需要扩展

tctc4869

2020-04-07 17:22

@JFinal 那这种情况我该怎么办,我写了一个实体A,专门处理特殊的数据,实体A数据更新到数据库里的方式就是转成字符串存到varchar里。在jdbc调用更新写入之前,将实体A其转成t特殊格式的字符串存到varchar里。不能自动调用的话,
那每次编写Set方法处理特殊数据的实体,都要调用手动写转换方法啊

zhangtianxiao

2020-04-07 18:55

@tctc4869 jdbc传参的场景, 不是框架能干涉的, 你可能需要关注 com.jfinal.plugin.activerecord.dialect.PostgreSqlDialect#fillStatement

tctc4869

2020-04-07 19:33

@zhangtianxiao 我准确的说明,是调用jdbc之前的过程,持久化框架是能干这件事的,不就有一个例子么,mybatis它就是啊,这个框架提供了typeHandler抽象类,让开发者在jdbc访问数据库之前,实现自己自定义类型转换处理。那持久层框架应该是能干的。

tctc4869

2020-04-08 09:47

@zhangtianxiao 那调用fillStatement这个方法的方法又在哪个类?因为也想要根据继承Model的类的class配上内部注解来实现自动转换

tctc4869

2020-04-08 09:58

@zhangtianxiao

Record user = new Record().set(TestEntity.Pc_Name, "James").set(TestEntity.Pc_Age,18)
.set(TestEntity.Pc_Id, 3)
.set(TestEntity.Pc_TestType, new TestChild("1","3"))
.set(TestEntity.Pc_T_time, LocalTime.now());
Db.update("ttest", user);

public class MyPostgreSqlDialect extends PostgreSqlDialect {

@Override
public void fillStatement(PreparedStatement pst, List paras) throws SQLException {

System.out.println("777");

fillStatementHandleDateType(pst, paras);
}

@Override
public void fillStatement(PreparedStatement pst, Object... paras) throws SQLException {

System.out.println(12346);
for(Object i :paras) {
if(i.getClass()==TestChild.class) {
TestChild tc=(TestChild) i;
i=tc.getNum_id()+tc.getUu_id();
}
}

fillStatementHandleDateType(pst, paras);
}
}


Exception in thread "main" com.jfinal.plugin.activerecord.ActiveRecordException: org.postgresql.util.PSQLException: Can't infer the SQL type to use for an instance of com.xxx.module.test.TestChild. Use setObject() with an explicit Types value to specify the type to use.
at com.jfinal.plugin.activerecord.DbPro.update(DbPro.java:698)
at com.jfinal.plugin.activerecord.DbPro.update(DbPro.java:713)
at com.jfinal.plugin.activerecord.Db.update(Db.java:506)
at myJfinalUndertow.RecordTest.main(RecordTest.java:38)
Caused by: org.postgresql.util.PSQLException: Can't infer the SQL type to use for an instance of com.tc.module.test.TestChild. Use setObject() with an explicit Types value to specify the type to use.
at org.postgresql.jdbc.PgPreparedStatement.setObject(PgPreparedStatement.java:978)
at com.alibaba.druid.pool.DruidPooledPreparedStatement.setObject(DruidPooledPreparedStatement.java:480)
at com.jfinal.plugin.activerecord.dialect.Dialect.fillStatementHandleDateType(Dialect.java:312)
at common.persistence.typehandler.MyPostgreSqlDialect.fillStatement(MyPostgreSqlDialect.java:31)
at com.jfinal.plugin.activerecord.DbPro.update(DbPro.java:277)
at com.jfinal.plugin.activerecord.DbPro.update(DbPro.java:678)
at com.jfinal.plugin.activerecord.DbPro.update(DbPro.java:696)
... 3 more

刚刚测试了一下,将实体类型的数据用于Set,进行update测试,但无法到达fillStatement这里,在之前抛出了异常

热门反馈

扫码入社