场景:
角色[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")
}
)
定义类型并指定参数化的集合类型、元素类型和分隔符。
相关推荐
1. hibernate杂谈 ...1.5 Hibernate自定义类型 集合--->字符串 存储 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 http://jinnianshilongnian.iteye.com 第 2
目 录 1. hibernate杂谈 1.1 Hibernate存取JSON数据(换一种思路来...1.5 Hibernate自定义类型 集合--->字符串 存储 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .54
- `uuid.hex`、`uuid.string`:生成128位UUID,前者以16进制字符串表示,后者是未编码的UUID。 - `foreign`:使用关联表的字段作为主键。 8. `name`:指定持久化类的全限定名,如果不指定,Hibernate会认为这是一...
Hibernate允许开发者自定义数据类型,将复杂对象如集合转化为字符串存储。这通常涉及到实现`UserType`接口,覆盖其中的方法如`sqlTypes()`、`returnedClass()`等。自定义类型可以用于处理非标准数据库字段,如存储...
在Java编程中,枚举类型(Enum)是一种强大的工具,它可以提供固定的、预定义的值集合,常用于表示有限数量的状态或选项。在关系数据库中,我们可能也需要将这些枚举值存储为列的特定类型。Hibernate,作为Java中...
- **基本值类型**:字符串、数字等简单数据类型的映射。 - **持久化枚举(Persistent enum)类型**:枚举类型的映射策略。 - **自定义值类型**:用户定义的复杂数据类型的映射。 - **映射到"任意"(any)类型**:...
- **查询语言替换**:允许在运行时替换 HQL 查询中的部分字符串。 - **Hibernate 统计信息**:启用 Hibernate 的统计功能以收集执行信息。 - **日志**:设置 Hibernate 日志记录,以调试或监控应用程序行为。 - *...
允许使用自定义字符串替换查询中的特定标识符。 - **3.4.6 Hibernate统计信息** 开启统计信息收集,以便于性能分析和调试。 - **3.5 日志记录** 配置日志框架,如Log4j或SLF4J。 - **3.6 实现NamingStrategy**...
- Hibernate支持多种基本类型映射,如整型、字符串、日期等,同时也支持复杂类型的映射,如集合、自定义类型等。 - 映射文件(通常是`.hbm.xml`)定义了Java对象与数据库表之间的关系,包括字段对应、主键设置、...
- **标识符属性**:每个实体类都需要一个唯一标识符属性,通常使用整型或字符串类型。 - **构造函数**:提供默认构造函数和其他必要构造函数以满足不同场景的需求。 **1.3 映射关系** - **映射文件**:使用 XML ...
- **基本值类型**:如字符串、整型等。 - **持久化枚举类型**:将枚举类型映射到数据库。 - **自定义值类型**:允许开发者定义自己的类型转换器。 - **任意类型映射**:将任意类型映射到数据库字段。 #### 五、SQL...
- 替换查询语言:允许在查询字符串中使用占位符来替换具体的值。 - 统计信息收集:收集和显示有关 Hibernate 执行情况的信息。 #### 六、日志记录 - Hibernate 提供了多种日志记录框架的支持,如 Log4j、SLF4J ...
- **自动类型提升**:较小的基本类型可以自动转换为较大的基本类型,例如`byte` -> `short` -> `int` -> `long` -> `float` -> `double`。 - **强制类型转换**:当从大类型向小类型转换时,需要进行强制类型转换,...
Criteria查询则提供了一种更加面向对象的查询方式,无需编写字符串查询,而是通过构建查询对象进行操作。 八、缓存机制 Hibernate2提供了第一级缓存(Session级别的缓存)和第二级缓存(SessionFactory级别的缓存)...
1. **@ElementCollection**:用于非关联的简单元素集合,如字符串列表或枚举集。 2. **@OneToMany(cascade=CascadeType.ALL, mappedBy="property")**:配置一对多关系,`mappedBy`指定在子实体中的反向引用属性。 ...