场景:
角色[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")
}
)
定义类型并指定参数化的集合类型、元素类型和分隔符。