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

场景:

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

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

 

方案:

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

 

Java代码   收藏代码
  1. /** 
  2.  * Copyright (c) 2005-2012 https://github.com/zhangkaitao 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  */  
  6. package com.sishuok.es.common.repository.hibernate.type;  
  7.   
  8. import com.google.common.collect.Lists;  
  9. import org.apache.commons.beanutils.ConvertUtils;  
  10. import org.apache.commons.lang3.StringUtils;  
  11. import org.hibernate.HibernateException;  
  12. import org.hibernate.engine.spi.SessionImplementor;  
  13. import org.hibernate.usertype.EnhancedUserType;  
  14. import org.hibernate.usertype.ParameterizedType;  
  15. import org.hibernate.usertype.UserType;  
  16.   
  17. import java.io.*;  
  18. import java.sql.PreparedStatement;  
  19. import java.sql.ResultSet;  
  20. import java.sql.SQLException;  
  21. import java.sql.Types;  
  22. import java.util.Collection;  
  23. import java.util.List;  
  24. import java.util.Properties;  
  25.   
  26. /** 
  27.  * 将List转换为指定分隔符分隔的字符串存储 List的元素类型只支持常见的数据类型 可参考{@link org.apache.commons.beanutils.ConvertUtilsBean} 
  28.  * <p>User: Zhang Kaitao 
  29.  * <p>Date: 13-4-16 上午8:32 
  30.  * <p>Version: 1.0 
  31.  */  
  32. public class CollectionToStringUserType implements UserType, ParameterizedType, Serializable {  
  33.   
  34.     /** 
  35.      * 默认, 
  36.      */  
  37.     private String separator;  
  38.     /** 
  39.      * 默认 java.lang.Long 
  40.      */  
  41.     private Class elementType;  
  42.     /** 
  43.      * 默认 ArrayList 
  44.      */  
  45.     private Class collectionType;  
  46.   
  47.   
  48.   
  49.     @Override  
  50.     public void setParameterValues(Properties parameters) {  
  51.         String separator = (String)parameters.get("separator");  
  52.         if(!StringUtils.isEmpty(separator)) {  
  53.             this.separator = separator;  
  54.         } else {  
  55.             this.separator = ",";  
  56.         }  
  57.   
  58.         String collectionType = (String)parameters.get("collectionType");  
  59.         if(!StringUtils.isEmpty(collectionType)) {  
  60.             try {  
  61.                 this.collectionType = Class.forName(collectionType);  
  62.             } catch (ClassNotFoundException e) {  
  63.                 throw new HibernateException(e);  
  64.             }  
  65.         } else {  
  66.             this.collectionType = java.util.ArrayList.class;  
  67.         }  
  68.   
  69.         String elementType = (String)parameters.get("elementType");  
  70.         if(!StringUtils.isEmpty(elementType)) {  
  71.             try {  
  72.                 this.elementType = Class.forName(elementType);  
  73.             } catch (ClassNotFoundException e) {  
  74.                 throw new HibernateException(e);  
  75.             }  
  76.         } else {  
  77.             this.elementType = Long.TYPE;  
  78.         }  
  79.     }  
  80.   
  81.     @Override  
  82.     public int[] sqlTypes() {  
  83.         return new int[] {Types.VARCHAR};  
  84.     }  
  85.   
  86.     @Override  
  87.     public Class returnedClass() {  
  88.         return collectionType;  
  89.     }  
  90.   
  91.     @Override  
  92.     public boolean equals(Object o, Object o1) throws HibernateException {  
  93.         if (o == o1) {  
  94.             return true;  
  95.         }  
  96.         if (o == null || o == null) {  
  97.             return false;  
  98.         }  
  99.   
  100.         return o.equals(o1);  
  101.     }  
  102.   
  103.     @Override  
  104.     public int hashCode(Object o) throws HibernateException {  
  105.         return o.hashCode();  
  106.     }  
  107.   
  108.   
  109.     /** 
  110.      * 从JDBC ResultSet读取数据,将其转换为自定义类型后返回 
  111.      * (此方法要求对克能出现null值进行处理) 
  112.      * names中包含了当前自定义类型的映射字段名称 
  113.      * @param names 
  114.      * @param owner 
  115.      * @return 
  116.      * @throws HibernateException 
  117.      * @throws java.sql.SQLException 
  118.      */  
  119.     @Override  
  120.     public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) throws HibernateException, SQLException {  
  121.         String valueStr = rs.getString(names[0]);  
  122.         if(StringUtils.isEmpty(valueStr)) {  
  123.             return null;  
  124.         }  
  125.   
  126.         String[] values = StringUtils.split(valueStr, separator);  
  127.   
  128.         Collection result = newCollection();  
  129.   
  130.         for(String value : values) {  
  131.             result.add(ConvertUtils.convert(value, elementType));  
  132.         }  
  133.         return result;  
  134.   
  135.     }  
  136.   
  137.     private Collection newCollection() {  
  138.         try {  
  139.             return (Collection)collectionType.newInstance();  
  140.         } catch (Exception e) {  
  141.             throw new HibernateException(e);  
  142.         }  
  143.     }  
  144.   
  145.     /** 
  146.      * 本方法将在Hibernate进行数据保存时被调用 
  147.      * 我们可以通过PreparedStateme将自定义数据写入到对应的数据库表字段 
  148.      */  
  149.     @Override  
  150.     public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws HibernateException, SQLException {  
  151.         String valueStr;  
  152.         if(value == null) {  
  153.             valueStr = "";  
  154.         } else {  
  155.             valueStr = StringUtils.join((Collection)value, separator);  
  156.         }  
  157.         st.setString(index, valueStr);  
  158.     }  
  159.   
  160.   
  161.   
  162.     /** 
  163.      * 提供自定义类型的完全复制方法 
  164.      * 本方法将用构造返回对象 
  165.      * 当nullSafeGet方法调用之后,我们获得了自定义数据对象,在向用户返回自定义数据之前, 
  166.      * deepCopy方法将被调用,它将根据自定义数据对象构造一个完全拷贝,并将此拷贝返回给用户 
  167.      * 此时我们就得到了自定义数据对象的两个版本,第一个是从数据库读出的原始版本,其二是我们通过 
  168.      * deepCopy方法构造的复制版本,原始的版本将有Hibernate维护,复制版由用户使用。原始版本用作 
  169.      * 稍后的脏数据检查依据;Hibernate将在脏数据检查过程中将两个版本的数据进行对比(通过调用 
  170.      * equals方法),如果数据发生了变化(equals方法返回false),则执行对应的持久化操作 
  171.      * 
  172.      * @param o 
  173.      * @return 
  174.      * @throws HibernateException 
  175.      */  
  176.     @Override  
  177.     public Object deepCopy(Object o) throws HibernateException {  
  178.         if(o == nullreturn null;  
  179.         Collection copyCollection = newCollection();  
  180.         copyCollection.addAll((Collection)o);  
  181.         return copyCollection;  
  182.     }  
  183.   
  184.     /** 
  185.      * 本类型实例是否可变 
  186.      * @return 
  187.      */  
  188.     @Override  
  189.     public boolean isMutable() {  
  190.         return true;  
  191.     }  
  192.   
  193.     /* 序列化 */  
  194.     @Override  
  195.     public Serializable disassemble(Object value) throws HibernateException {  
  196.         return ((Serializable)value);  
  197.     }  
  198.   
  199.     /* 反序列化 */  
  200.     @Override  
  201.     public Object assemble(Serializable cached, Object owner) throws HibernateException {  
  202.         return cached;  
  203.     }  
  204.   
  205.     @Override  
  206.     public Object replace(Object original, Object target, Object owner) throws HibernateException {  
  207.         return original;  
  208.     }  
  209.   
  210. }  

 

 

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

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

 

 

使用:

 

Java代码   收藏代码
  1. /** 
  2.  * 此处没有使用关联 是为了提高性能(后续会挨着查询资源和权限列表,因为有缓存,数据量也不是很大 所以性能不会差) 
  3.  * <p>User: Zhang Kaitao 
  4.  * <p>Date: 13-4-5 下午2:04 
  5.  * <p>Version: 1.0 
  6.  */  
  7.   
  8. @TypeDef(  
  9.         name = "SetToStringUserType",  
  10.         typeClass = CollectionToStringUserType.class,  
  11.         parameters = {  
  12.                  @Parameter(name = "separator", value = ","),  
  13.                  @Parameter(name = "collectionType", value = "java.util.HashSet"),  
  14.                  @Parameter(name = "elementType", value = "java.lang.Long")  
  15.         }  
  16. )  
  17. @Entity  
  18. @Table(name = "sys_role_resource_permission")  
  19. public class RoleResourcePermission extends BaseEntity<Long> {  
  20.   
  21.     /** 
  22.      * 角色id 
  23.      */  
  24.     @ManyToOne(optional = true, fetch = FetchType.EAGER)  
  25.     @Fetch(FetchMode.SELECT)  
  26.     private Role role;  
  27.   
  28.     /** 
  29.      * 资源id 
  30.      */  
  31.     @Column(name ="resource_id")  
  32.     private Long resourceId;  
  33.   
  34.     /** 
  35.      * 权限id列表 
  36.      * 数据库通过字符串存储 逗号分隔 
  37.      */  
  38.     @Column(name ="permission_ids")  
  39.     @Type(type = "SetToStringUserType")  
  40.     private Set<Long> permissionIds;  
  41.   
  42.     public RoleResourcePermission() {  
  43.     }  

 

@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")

        }

)

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值