首页 > Java开发 > Hibernate自定义类型 集合—>字符串 存储

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

 

 

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. @TypeDef(
  8.         name = "SetToStringUserType",
  9.         typeClass = CollectionToStringUserType.class,
  10.         parameters = {
  11.                  @Parameter(name = "separator", value = ","),
  12.                  @Parameter(name = "collectionType", value = "java.util.HashSet"),
  13.                  @Parameter(name = "elementType", value = "java.lang.Long")
  14.         }
  15. )
  16. @Entity
  17. @Table(name = "sys_role_resource_permission")
  18. public class RoleResourcePermission extends BaseEntity<Long> {
  19.     /**
  20.      * 角色id
  21.      */
  22.     @ManyToOne(optional = true, fetch = FetchType.EAGER)
  23.     @Fetch(FetchMode.SELECT)
  24.     private Role role;
  25.     /**
  26.      * 资源id
  27.      */
  28.     @Column(name ="resource_id")
  29.     private Long resourceId;
  30.     /**
  31.      * 权限id列表
  32.      * 数据库通过字符串存储 逗号分隔
  33.      */
  34.     @Column(name ="permission_ids")
  35.     @Type(type = "SetToStringUserType")
  36.     private Set<Long> permissionIds;
  37.     public RoleResourcePermission() {
  38.     }

 

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

}

)

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


本文固定链接: http://www.devba.com/index.php/archives/279.html | 开发吧

报歉!评论已关闭.