Hibernate自定义类型 集合--->字符串 存储

编程技术  /  houtizong 发布于 3年前   57

场景:

角色[1]-----[*](资源[1]---[*]权限)

某个角色 具有 某个资源的 某些权限,当然此处都是多对多 为了好理解 暂时1---*。 这里是资源-对应-多个权限,但是权限一般不会很多,而且我们一般也不会根据权限去查找,因此没必要做个关联表,此处我们可以使用字符串如1,2,3,4来存储其id,这样可以有效减少中间表数量 提高效率。

 

方案:

如果不想在程序中拼接这种字符串 我们可以考虑使用Hibernate自定义数据类型; 即把集合类型--->某个分隔符连接的字符串

 

/** * Copyright (c) 2005-2012 https://github.com/zhangkaitao * * Licensed under the Apache License, Version 2.0 (the "License"); */package com.sishuok.es.common.repository.hibernate.type;import com.google.common.collect.Lists;import org.apache.commons.beanutils.ConvertUtils;import org.apache.commons.lang3.StringUtils;import org.hibernate.HibernateException;import org.hibernate.engine.spi.SessionImplementor;import org.hibernate.usertype.EnhancedUserType;import org.hibernate.usertype.ParameterizedType;import org.hibernate.usertype.UserType;import java.io.*;import java.sql.PreparedStatement;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Types;import java.util.Collection;import java.util.List;import java.util.Properties;/** * 将List转换为指定分隔符分隔的字符串存储 List的元素类型只支持常见的数据类型 可参考{@link org.apache.commons.beanutils.ConvertUtilsBean} * <p>User: Zhang Kaitao * <p>Date: 13-4-16 上午8:32 * <p>Version: 1.0 */public class CollectionToStringUserType implements UserType, ParameterizedType, Serializable {    /**     * 默认,     */    private String separator;    /**     * 默认 java.lang.Long     */    private Class elementType;    /**     * 默认 ArrayList     */    private Class collectionType;    @Override    public void setParameterValues(Properties parameters) {        String separator = (String)parameters.get("separator");        if(!StringUtils.isEmpty(separator)) {            this.separator = separator;        } else {            this.separator = ",";        }        String collectionType = (String)parameters.get("collectionType");        if(!StringUtils.isEmpty(collectionType)) {            try {                this.collectionType = Class.forName(collectionType);            } catch (ClassNotFoundException e) {                throw new HibernateException(e);            }        } else {            this.collectionType = java.util.ArrayList.class;        }        String elementType = (String)parameters.get("elementType");        if(!StringUtils.isEmpty(elementType)) {            try {                this.elementType = Class.forName(elementType);            } catch (ClassNotFoundException e) {                throw new HibernateException(e);            }        } else {            this.elementType = Long.TYPE;        }    }    @Override    public int[] sqlTypes() {        return new int[] {Types.VARCHAR};    }    @Override    public Class returnedClass() {        return collectionType;    }    @Override    public boolean equals(Object o, Object o1) throws HibernateException {        if (o == o1) {            return true;        }        if (o == null || o == null) {            return false;        }        return o.equals(o1);    }    @Override    public int hashCode(Object o) throws HibernateException {        return o.hashCode();    }    /**     * 从JDBC ResultSet读取数据,将其转换为自定义类型后返回     * (此方法要求对克能出现null值进行处理)     * names中包含了当前自定义类型的映射字段名称     * @param names     * @param owner     * @return     * @throws HibernateException     * @throws java.sql.SQLException     */    @Override    public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) throws HibernateException, SQLException {        String valueStr = rs.getString(names[0]);        if(StringUtils.isEmpty(valueStr)) {            return null;        }        String[] values = StringUtils.split(valueStr, separator);        Collection result = newCollection();        for(String value : values) {            result.add(ConvertUtils.convert(value, elementType));        }        return result;    }    private Collection newCollection() {        try {            return (Collection)collectionType.newInstance();        } catch (Exception e) {            throw new HibernateException(e);        }    }    /**     * 本方法将在Hibernate进行数据保存时被调用     * 我们可以通过PreparedStateme将自定义数据写入到对应的数据库表字段     */    @Override    public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws HibernateException, SQLException {        String valueStr;        if(value == null) {            valueStr = "";        } else {            valueStr = StringUtils.join((Collection)value, separator);        }        st.setString(index, valueStr);    }    /**     * 提供自定义类型的完全复制方法     * 本方法将用构造返回对象     * 当nullSafeGet方法调用之后,我们获得了自定义数据对象,在向用户返回自定义数据之前,     * deepCopy方法将被调用,它将根据自定义数据对象构造一个完全拷贝,并将此拷贝返回给用户     * 此时我们就得到了自定义数据对象的两个版本,第一个是从数据库读出的原始版本,其二是我们通过     * deepCopy方法构造的复制版本,原始的版本将有Hibernate维护,复制版由用户使用。原始版本用作     * 稍后的脏数据检查依据;Hibernate将在脏数据检查过程中将两个版本的数据进行对比(通过调用     * equals方法),如果数据发生了变化(equals方法返回false),则执行对应的持久化操作     *     * @param o     * @return     * @throws HibernateException     */    @Override    public Object deepCopy(Object o) throws HibernateException {        if(o == null) return null;        Collection copyCollection = newCollection();        copyCollection.addAll((Collection)o);        return copyCollection;    }    /**     * 本类型实例是否可变     * @return     */    @Override    public boolean isMutable() {        return true;    }    /* 序列化 */    @Override    public Serializable disassemble(Object value) throws HibernateException {        return ((Serializable)value);    }    /* 反序列化 */    @Override    public Object assemble(Serializable cached, Object owner) throws HibernateException {        return cached;    }    @Override    public Object replace(Object original, Object target, Object owner) throws HibernateException {        return original;    }}

 

 

1、setParameterValues 作用是参数化 集合类型 和分隔符 不写死了

2、deepCopy必须复制一份 否则即使我们改了 session也检测不到脏数据

 

 

使用:

 

/** * 此处没有使用关联 是为了提高性能(后续会挨着查询资源和权限列表,因为有缓存,数据量也不是很大 所以性能不会差) * <p>User: Zhang Kaitao * <p>Date: 13-4-5 下午2:04 * <p>Version: 1.0 */@TypeDef(        name = "SetToStringUserType",        typeClass = CollectionToStringUserType.class,        parameters = {                 @Parameter(name = "separator", value = ","),                 @Parameter(name = "collectionType", value = "java.util.HashSet"),                 @Parameter(name = "elementType", value = "java.lang.Long")        })@Entity@Table(name = "sys_role_resource_permission")public class RoleResourcePermission extends BaseEntity<Long> {    /**     * 角色id     */    @ManyToOne(optional = true, fetch = FetchType.EAGER)    @Fetch(FetchMode.SELECT)    private Role role;    /**     * 资源id     */    @Column(name ="resource_id")    private Long resourceId;    /**     * 权限id列表     * 数据库通过字符串存储 逗号分隔     */    @Column(name ="permission_ids")    @Type(type = "SetToStringUserType")    private Set<Long> permissionIds;    public RoleResourcePermission() {    }

 

@TypeDef(

        name = "SetToStringUserType",

        typeClass = CollectionToStringUserType.class,

        parameters = {

                 @Parameter(name = "separator", value = ","),

                 @Parameter(name = "collectionType", value = "java.util.HashSet"),

                 @Parameter(name = "elementType", value = "java.lang.Long")

        }

)

定义类型并指定参数化的集合类型、元素类型和分隔符。

 

github代码

请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!

留言需要登陆哦

技术博客集 - 网站简介:
前后端技术:
后端基于Hyperf2.1框架开发,前端使用Bootstrap可视化布局系统生成

网站主要作用:
1.编程技术分享及讨论交流,内置聊天系统;
2.测试交流框架问题,比如:Hyperf、Laravel、TP、beego;
3.本站数据是基于大数据采集等爬虫技术为基础助力分享知识,如有侵权请发邮件到站长邮箱,站长会尽快处理;
4.站长邮箱:[email protected];

      订阅博客周刊 去订阅

文章归档

文章标签

友情链接

Auther ·HouTiZong
侯体宗的博客
© 2020 zongscan.com
版权所有ICP证 : 粤ICP备20027696号
PHP交流群 也可以扫右边的二维码
侯体宗的博客