实现思路
2023年8月11日
MybatisPlus自带更好的实现方案,@TableName和@TableField就可以满足...
所以,以下都是废代码...
1、获取要自动转换的实体类,需要有一个标识告诉程序
2、继承BaseTypeHandler,构造方法传入需要实现的实体类,从而实现数据库json格式的字段自动转换为实体类对象
具体实现代码如下,逻辑可能有误。
1,实体类定义
@TableName("connect_info")
@Slf4j
public class ConnectInfo implements Serializable {
private static final long serialVersionUID = 1L;
@TableId
private String pkId;
private String connectType;
private DataSourceBase connectMetaData;
public String getPkId() {
return pkId;
}
public void setPkId(String pkId) {
this.pkId = pkId;
}
public String getConnectType() {
return connectType;
}
public void setConnectType(String connectType) {
this.connectType = connectType;
}
public JSONObject getConnectMetaData() {
try {
return JsonMetaDataFactory.getMetaDataFromTarget(connectMetaData);
} catch (Exception e) {
log.error("获取失败:{}-{}",this.getClass().getName(),this.getPkId(),e);
throw new RuntimeException(""+e.getMessage());
}
}
public void setConnectMetaData(DataSourceBase connectMetaData) {
this.connectMetaData = connectMetaData;
}
}
@Data
@JsonMetaData
public class DataSourceBase {
private String user;
private String password;
private String url;
private DataSourceBaseAAA aaa;
}
@Data
public class DataSourceBaseAAA {
private String name;
private int age;
}
2,扫描实体类
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface JsonMetaData {
}
public interface JsonMetaDataSource {
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
@Import(ScanConfigImport.class)
public @interface EnableScanConfig {
String[] value() default {};
}
public class ScanConfigImport implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(EnableScanConfig.class.getName());
String[] value = (String [])annotationAttributes.get("value");
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(ScanConfig.class);
builder.addPropertyValue("packages", Sets.newHashSet(value));
builder.addConstructorArgValue(registry);
registry.registerBeanDefinition(ScanConfig.class.getName(),builder.getBeanDefinition());
}
}
public class ScanConfig
extends ClassPathBeanDefinitionScanner
implements BeanDefinitionRegistryPostProcessor{
@Setter
private Set<String> packages;
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
packages.addAll(AutoConfigurationPackages.get(beanFactory));
Map<String, TypeFilterNotMatch> beansOfType = beanFactory.getBeansOfType(TypeFilterNotMatch.class);
if(!CollectionUtils.isEmpty(beansOfType)){
super.resetFilters(false);
beansOfType.values().forEach(super::addIncludeFilter);
this.doScan(packages.toArray(new String[0]));
}
}
public ScanConfig(BeanDefinitionRegistry registry) {
super(registry);
}
}
public abstract class TypeFilterNotMatch implements TypeFilter {
abstract public void check(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException;
@Override
public boolean match(MetadataReader metadataReader,
MetadataReaderFactory metadataReaderFactory) throws IOException {
check(metadataReader,metadataReaderFactory);
return false;
}
}
@Component
@Slf4j
public class JsonMetaDataFilter extends TypeFilterNotMatch implements ResourceLoaderAware {
private ClassLoader classLoader;
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
classLoader = resourceLoader.getClassLoader();
}
@Getter
private final Set<Class<?>> setClass = new HashSet<>();
@Override
public void check(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) {
String className = metadataReader.getClassMetadata().getClassName();
boolean contains1 = metadataReader.getAnnotationMetadata()
.getAnnotationTypes().contains(JsonMetaData.class.getName());
if(contains1){
addClass(className);
return;
}
List<String> list = Lists.newArrayList(metadataReader.getClassMetadata().getInterfaceNames());
boolean contains2 = list.contains(JsonMetaDataSource.class.getName());
if(contains2){
addClass(className);
}
}
private void addClass(String className){
try {
Class<?> aClass = ClassUtils.forName(className, classLoader);
setClass.add(aClass);
}catch (Exception e){
log.error("JsonMetaDataFilter-addClass-加载失败:{}",className,e);
}
}
}
3,实体和JSON互转
public class JsonMetaDataTypeHandler<T> extends BaseTypeHandler<T> {
private final Class<T> cls;
public JsonMetaDataTypeHandler(Class<T> cls){
this.cls = cls;
}
@Override
public void setNonNullParameter(PreparedStatement preparedStatement, int i, T t, JdbcType jdbcType) throws SQLException {
String value = "";
try {
value = JsonMetaDataFactory.getMetaDataFromTarget(t)
.toString(SerializerFeature.DisableCircularReferenceDetect);
}catch (Exception e){
throw new RuntimeException(e.getMessage());
}
preparedStatement.setString(i,value);
}
@Override
public T getNullableResult(ResultSet resultSet, String columnName) throws SQLException {
String json = resultSet.getString(columnName);
T targetByJsonMetaData = null;
try {
targetByJsonMetaData =
JsonMetaDataFactory.getTargetByJsonMetaData(cls, JSONObject.parseObject(json));
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
return targetByJsonMetaData;
}
@Override
public T getNullableResult(ResultSet resultSet, int columnIndex) throws SQLException {
String json = resultSet.getString(columnIndex);
T targetByJsonMetaData = null;
try {
targetByJsonMetaData =
JsonMetaDataFactory.getTargetByJsonMetaData(cls, JSONObject.parseObject(json));
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
return targetByJsonMetaData;
}
@Override
public T getNullableResult(CallableStatement callableStatement, int columnIndex) throws SQLException {
String json = callableStatement.getString(columnIndex);
T targetByJsonMetaData = null;
try {
targetByJsonMetaData =
JsonMetaDataFactory.getTargetByJsonMetaData(cls, JSONObject.parseObject(json));
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
return targetByJsonMetaData;
}
}
public class JsonMetaDataFactory {
private static boolean saveMetaData = false;
public static <T> T getTargetByJsonMetaData(@NonNull Class<T> cls,
@NonNull JSONObject metaData) throws Exception{
T t = metaData.toJavaObject(cls);
if(!saveMetaData){
return t;
}
JsonMetaDataTarget<T> jsonMetaDataTarget = new JsonMetaDataTarget<>(t,metaData);
ProxyFactory factory = new ProxyFactory();
factory.setProxyTargetClass(true);
factory.setTarget(t);
factory.addAdvice(jsonMetaDataTarget);
return cls.cast(factory.getProxy());
}
public static <T> JSONObject getMetaDataFromTarget(@NonNull T t) throws Exception{
JsonMetaDataTarget<?> advice = ProxyFactoryUtils.getAdvisor(
ProxyFactoryUtils.getProxyFactory(t),JsonMetaDataTarget.class);
if(advice != null){
JSONObject metaData = advice.getMetaData();
JSONObject targetJson = (JSONObject)JSONObject.toJSON(advice.getTarget());
metaData.putAll(targetJson);
return metaData;
}else {
return (JSONObject)JSONObject.toJSON(t);
}
}
}
public class JsonMetaDataTarget<T> implements MethodInterceptor {
@Getter
private final T target;
@Getter
private final JSONObject metaData;
public JsonMetaDataTarget(T target, JSONObject metaData) {
this.target = target;
this.metaData = metaData;
}
@Nullable
@Override
public Object invoke(@Nonnull MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();
Object[] arguments = invocation.getArguments();
return method.invoke(target,arguments);
}
}
public class ProxyFactoryUtils {
private static volatile Field CGLIB$CALLBACK_0 = null;
private static final Object SYNC_CGLIB$CALLBACK_0 = new Object();
private static volatile Field ADVISED = null;
private static final Object SYNC_ADVISED = new Object();
public static <T> ProxyFactory getProxyFactory(T t) throws Exception {
if(AopUtils.isCglibProxy(t)){
if(CGLIB$CALLBACK_0 == null){
synchronized (SYNC_CGLIB$CALLBACK_0){
if(CGLIB$CALLBACK_0 == null){
Field cglib$CALLBACK_0 = ReflectionUtils.findField(t.getClass(), "CGLIB$CALLBACK_0");
if(cglib$CALLBACK_0 != null){
ReflectionUtils.makeAccessible(cglib$CALLBACK_0);
CGLIB$CALLBACK_0 = cglib$CALLBACK_0;
}
}
}
}
if(CGLIB$CALLBACK_0 == null){
return null;
}
Object fieldValue = CGLIB$CALLBACK_0.get(t);
if(ADVISED == null){
synchronized (SYNC_ADVISED){
if(ADVISED == null){
Field advised = ReflectionUtils.findField(fieldValue.getClass(), "advised");
if(advised != null){
ReflectionUtils.makeAccessible(advised);
ADVISED = advised;
}
}
}
}
if(ADVISED == null){
return null;
}
fieldValue = ADVISED.get(fieldValue);
if(fieldValue instanceof ProxyFactory){
return (ProxyFactory)fieldValue;
}
}
return null;
}
public static <T extends Advice> T getAdvisor(ProxyFactory factory, Class<T> cls){
if(factory != null){
Advisor[] advisors = factory.getAdvisors();
for(Advisor advisor : advisors){
if(advisor.getAdvice().getClass() == cls){
return cls.cast(advisor.getAdvice());
}
}
}
return null;
}
}
4,注册到Mybatis
@Configuration
@EnableScanConfig
public class MybatisConfig {
private List<SqlSessionFactory> sqlSessionFactories;
@Autowired
private JsonMetaDataFilter jsonMetaDataFilter;
@Autowired
public void setSqlSessionFactories(ObjectProvider<List<SqlSessionFactory>> sqlSessionFactories) {
this.sqlSessionFactories = sqlSessionFactories.getIfAvailable();
}
@PostConstruct
public void init(){
if(CollectionUtils.isEmpty(sqlSessionFactories)){
return;
}
for(SqlSessionFactory factory : sqlSessionFactories){
org.apache.ibatis.session.Configuration configuration = factory.getConfiguration();
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
typeHandlerRegistry.register(JSONObject.class, JdbcType.VARCHAR, FastjsonTypeHandler.class);
jsonMetaDataFilter.getSetClass().forEach(x->{
typeHandlerRegistry.register(x,(TypeHandler)new JsonMetaDataTypeHandler<>(x));
});
}
}
}