`
jinnianshilongnian
  • 浏览: 21499457 次
  • 性别: Icon_minigender_1
博客专栏
5c8dac6a-21dc-3466-8abb-057664ab39c7
跟我学spring3
浏览量:2417776
D659df3e-4ad7-3b12-8b9a-1e94abd75ac3
Spring杂谈
浏览量:3008085
43989fe4-8b6b-3109-aaec-379d27dd4090
跟开涛学SpringMVC...
浏览量:5638961
1df97887-a9e1-3328-b6da-091f51f886a1
Servlet3.1规范翻...
浏览量:259808
4f347843-a078-36c1-977f-797c7fc123fc
springmvc杂谈
浏览量:1597049
22722232-95c1-34f2-b8e1-d059493d3d98
hibernate杂谈
浏览量:250117
45b32b6f-7468-3077-be40-00a5853c9a48
跟我学Shiro
浏览量:5858157
Group-logo
跟我学Nginx+Lua开...
浏览量:701791
5041f67a-12b2-30ba-814d-b55f466529d5
亿级流量网站架构核心技术
浏览量:784902
社区版块
存档分类
最新评论

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

 
阅读更多

场景:

角色[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代码

3
2
分享到:
评论
3 楼 jacking124 2013-07-10  
jinnianshilongnian 写道
jacking124 写道
张哥,对hibernate提交主从表,同时更新另一张的信息,这样的过程中如果出现异常,回滚的时候,应该怎么设置,写提交过程的时候有什么特殊的要求否!

这个只要在一个事务中 自动回滚 无需特殊设置

好的,灰常感谢!
2 楼 jinnianshilongnian 2013-07-09  
jacking124 写道
张哥,对hibernate提交主从表,同时更新另一张的信息,这样的过程中如果出现异常,回滚的时候,应该怎么设置,写提交过程的时候有什么特殊的要求否!

这个只要在一个事务中 自动回滚 无需特殊设置
1 楼 jacking124 2013-07-09  
张哥,对hibernate提交主从表,同时更新另一张的信息,这样的过程中如果出现异常,回滚的时候,应该怎么设置,写提交过程的时候有什么特殊的要求否!

相关推荐

    张开涛 hibernate杂谈

    1. hibernate杂谈 ...1.5 Hibernate自定义类型 集合---&gt;字符串 存储 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 http://jinnianshilongnian.iteye.com 第 2

    hibernate杂谈

    目 录 1. hibernate杂谈 1.1 Hibernate存取JSON数据(换一种思路来...1.5 Hibernate自定义类型 集合---&gt;字符串 存储 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .54

    hibernate-mapping参数详解

    - `uuid.hex`、`uuid.string`:生成128位UUID,前者以16进制字符串表示,后者是未编码的UUID。 - `foreign`:使用关联表的字段作为主键。 8. `name`:指定持久化类的全限定名,如果不指定,Hibernate会认为这是一...

    hibernate 杂谈

    Hibernate允许开发者自定义数据类型,将复杂对象如集合转化为字符串存储。这通常涉及到实现`UserType`接口,覆盖其中的方法如`sqlTypes()`、`returnedClass()`等。自定义类型可以用于处理非标准数据库字段,如存储...

    hibernate映射枚举类型

    在Java编程中,枚举类型(Enum)是一种强大的工具,它可以提供固定的、预定义的值集合,常用于表示有限数量的状态或选项。在关系数据库中,我们可能也需要将这些枚举值存储为列的特定类型。Hibernate,作为Java中...

    Hibernate 参考文档

    - **基本值类型**:字符串、数字等简单数据类型的映射。 - **持久化枚举(Persistent enum)类型**:枚举类型的映射策略。 - **自定义值类型**:用户定义的复杂数据类型的映射。 - **映射到"任意"(any)类型**:...

    hibernate3.6 文档(pdf 格式)

    - **查询语言替换**:允许在运行时替换 HQL 查询中的部分字符串。 - **Hibernate 统计信息**:启用 Hibernate 的统计功能以收集执行信息。 - **日志**:设置 Hibernate 日志记录,以调试或监控应用程序行为。 - *...

    hibernate参考文档

    允许使用自定义字符串替换查询中的特定标识符。 - **3.4.6 Hibernate统计信息** 开启统计信息收集,以便于性能分析和调试。 - **3.5 日志记录** 配置日志框架,如Log4j或SLF4J。 - **3.6 实现NamingStrategy**...

    精通Hibernate第二章PPT

    - Hibernate支持多种基本类型映射,如整型、字符串、日期等,同时也支持复杂类型的映射,如集合、自定义类型等。 - 映射文件(通常是`.hbm.xml`)定义了Java对象与数据库表之间的关系,包括字段对应、主键设置、...

    Hibernate Reference Documentation

    - **标识符属性**:每个实体类都需要一个唯一标识符属性,通常使用整型或字符串类型。 - **构造函数**:提供默认构造函数和其他必要构造函数以满足不同场景的需求。 **1.3 映射关系** - **映射文件**:使用 XML ...

    Hibernate Reference Documentation(Hibernate参考手册)

    - **基本值类型**:如字符串、整型等。 - **持久化枚举类型**:将枚举类型映射到数据库。 - **自定义值类型**:允许开发者定义自己的类型转换器。 - **任意类型映射**:将任意类型映射到数据库字段。 #### 五、SQL...

    Hibernate References

    - 替换查询语言:允许在查询字符串中使用占位符来替换具体的值。 - 统计信息收集:收集和显示有关 Hibernate 执行情况的信息。 #### 六、日志记录 - Hibernate 提供了多种日志记录框架的支持,如 Log4j、SLF4J ...

    java知识梳理

    - **自动类型提升**:较小的基本类型可以自动转换为较大的基本类型,例如`byte` -&gt; `short` -&gt; `int` -&gt; `long` -&gt; `float` -&gt; `double`。 - **强制类型转换**:当从大类型向小类型转换时,需要进行强制类型转换,...

    Hibernate2 参考文档

    Criteria查询则提供了一种更加面向对象的查询方式,无需编写字符串查询,而是通过构建查询对象进行操作。 八、缓存机制 Hibernate2提供了第一级缓存(Session级别的缓存)和第二级缓存(SessionFactory级别的缓存)...

    Hibernate关联关系的CRUD和集合映射(annotation)

    1. **@ElementCollection**:用于非关联的简单元素集合,如字符串列表或枚举集。 2. **@OneToMany(cascade=CascadeType.ALL, mappedBy="property")**:配置一对多关系,`mappedBy`指定在子实体中的反向引用属性。 ...

Global site tag (gtag.js) - Google Analytics