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

Hibernate存取JSON数据(换一种思路来存取数据)

阅读更多

 


一、场景

public class OrderModel {

private List<String> favorableDescList;

}

 

订单中会存储一些优惠信息,方便页面展示时使用,如:

1、满10050

2、参与【老会员真情回馈——精品课程体验活动】,仅需支付200.00学币

3、【Oracle + PL/SQL 实战】套装课程的【抢购】活动,优惠120.00学币

……等等

 

如图所示,我们在页面给用户展示他们参与的优惠信息:


二、分析

如上优惠信息有如下特点:

1、只用于展示,不会涉及修改;

2、一旦订单支付成功,不会再改变;

3、数据量不会很大。

三、解决方案

1、最简单的解决方案是关联表:


 但这种解决方案需要连表进行查询,感觉是没有必要的,毕竟只是展示数据,用关联表有点杀鸡用牛刀的感觉。

 

 

2JSON解决方案:

 

通过如上思路我们可以解决许多类似的问题。

 

 

3、代码示例:

 

1、模型类:

public class OrderModel {
    @Type(type = "cn.javass.framework.hibernate.type.JsonType")  //①
    private List<String> favorableDescList;
}

 ①处使用我们自定义的Hibernate类型来进行转换,上边代码只有一部分

 

2、自定义JsonType

package cn.javass.framework.hibernate.type;
//省略import
public class JsonType implements UserType, Serializable {
    private String json;
    @Override
    public int[] sqlTypes() {
        return new int[] {Hibernate.STRING.sqlType()};
    }
    @Override
    public Class returnedClass() {
        return JsonList.class;
    }
    @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 resultSet
  * @param names
  * @param owner
  * @return
  * @throws HibernateException
  * @throws SQLException
  */
    @Override
    public Object nullSafeGet(ResultSet resultSet, String[] names, Object owner) throws HibernateException, SQLException {
        String json = resultSet.getString(names[0]);
        if(json == null || json.trim().length() == 0) {
            return new JsonList();
        }
        return JSONArray.toList(JSONArray.fromObject(json), JsonList.class);
    }

     /**
  * 本方法将在Hibernate进行数据保存时被调用
  * 我们可以通过PreparedStateme将自定义数据写入到对应的数据库表字段
  * @param preparedStatement
  * @param value
  * @param i
  * @throws HibernateException
  * @throws SQLException
  */
    @Override
    public void nullSafeSet(PreparedStatement preparedStatement, Object value, int i) throws HibernateException, SQLException {
        if(value == null) {
            preparedStatement.setNull(i, Hibernate.STRING.sqlType());
        } else {
            preparedStatement.setString(i, JSONArray.fromObject(value).toString());
        }
    }

    /**
     * 提供自定义类型的完全复制方法
     * 本方法将用构造返回对象
     * 当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;
        JsonList jsonList = new JsonList();
        jsonList.addAll((List)o);
        return jsonList;
    }

    /**
     * 本类型实例是否可变
     * @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;
    }
}

 JSON框架使用的是json-lib 2.1

 

3、自定义JsonList

package cn.javass.framework.hibernate;
public class JsonList<T> extends ArrayList implements Cloneable {
}

 

就这么简单,欢迎大家讨论。

 

订单Model属于公司机密,不方便附上,其他两个类请附件下载(JsonTypeJsonList的源代码)。

 

 

 

 

 

有人说有性能问题,我写了个测试用例:

 

测试机器:CPU:p8700(双核@2.53GHZ)  内存:2G

 

一、插入

1、JSON方式插入10w

create 100000 elapsed time(millis):21031

2、关联表插入10w

create 100000 elapsed time(millis):79219

 

JSON性能远远好于关联表,关联表要插入两个表。

 

 

二、查询

1、JSON方式分页(100条一页)查询10w

select 100000 elapsed time(millis):146047

2、关联表分页(100条一页)查询10w

select 100000 elapsed time(millis):275375

 

JSON性能远远好于关联表,关联表需要join连表查询。

 

JSON方式的缺点:分析统计等查询是鸡肋、大数据量是鸡肋(一列存储数据量不可能太大)。

 

我的应用场景:优惠信息、购物车持久化(每个用户购物车最多50条)。

 

附件中的 performance.rar是测试用例,欢迎测试拍砖。。

下载地址:performance.rar 

                  hibernate.rar 

 


///////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////新场景补充///////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////////////////////////////

购物车持久化:

1、用户没有登录:存储到cookie中

2、用户登录后:   存储到数据库中

 

分析:

 购物车是用户绑定的,一个用户一个

购物车最多只允许存50个(因为cookie大小有限)

 

实现:

1、关联表:购物车-----购物车明细,又是典型一对多,,需要频繁操作关联表

2、JSON:只要一张购物车表,用户数据通过JSON存数据库(如 [{productUuid:1, number:2}, {productUuid:2, number:2}])

 

使用JSON方式效率肯定是最高的,虽然有转换过程但是在内存中,比读写磁盘肯定快的多。看上边的测试用例。这种方式的好处不需要单独的关联表,省了一张表,何乐而不为。

 

29
10
分享到:
评论
18 楼 freeboat 2015-03-18  
freeboat 写道
这种方法用过之后一直有一个问题没解决,就是查询出来之后总是会自动更新这个字段的内容,equals中第一个参数总是为null,请指点,不胜感激。

就是查询出一条持久化对象,如果不进行clear,那么虽然不做任何操作,那么这个自定义的json字段也会进行update,而且我这里是update2次,个中原因一直没查出来,现在只能是手工clear控制,可总有漏网之鱼
17 楼 freeboat 2015-03-18  
这种方法用过之后一直有一个问题没解决,就是查询出来之后总是会自动更新这个字段的内容,equals中第一个参数总是为null,请指点,不胜感激。
16 楼 saviorzuo 2014-11-27  
我觉得这篇博客很好,是博主自己项目中的经验,希望楼主多写点关于项目中的经验拿出来分享下
15 楼 freeboat 2014-03-06  
整得太好!继续整!学到好多东西。
14 楼 wjpiao 2013-06-05  
群主忠实粉儿!!!
13 楼 jinnianshilongnian 2012-04-26  
rensanning 写道
稍微研究过JSON的人都知道Jackson比Json-lib性能就不在一个量级上。

此处主要是 JSON方式 和 关联表的 对比 
12 楼 jinnianshilongnian 2012-04-26  
rensanning 写道
稍微研究过JSON的人都知道Jackson比Json-lib性能就不在一个量级上。

而且高出很多倍
11 楼 rensanning 2012-04-26  
稍微研究过JSON的人都知道Jackson比Json-lib性能就不在一个量级上。
10 楼 lucifer833 2012-04-25  
我想楼主说的意思是,用json串存储优惠信息比用一张表存储优惠信息好,对不?
9 楼 jinnianshilongnian 2012-04-25  
hlxinyan 写道
不明白。
将  List<String> favorableDescList  这个转化只是为了用json存储 优惠信息吗.
还是解决了 两个表关联的问题。

为什么不将 订单优惠信息 ,作为 一个 字段放到订单表中,多个item 之间用,分隔。

这样生成Order model 时,不就与订单优惠信息了吗?
 

“,” 可能包含在我们的信息中啊,思路是一样的 实现方式不同
8 楼 hlxinyan 2012-04-25  
不明白。
将  List<String> favorableDescList  这个转化只是为了用json存储 优惠信息吗.
还是解决了 两个表关联的问题。

为什么不将 订单优惠信息 ,作为 一个 字段放到订单表中,多个item 之间用,分隔。

这样生成Order model 时,不就与订单优惠信息了吗?
 
7 楼 jinnianshilongnian 2012-04-25  
lucifer833 写道
为什么不写一个可以把Model转换成json的方法,然后存到数据库中,使用时再转回来的方法,反而要自定一个JsonType类型呢?楼主,探讨一下。给个原因~~~

因为我没有用到 

我用到的是 List<String> -----> JSON

当然这种想法可以扩展
6 楼 jinnianshilongnian 2012-04-25  
lucifer833 写道
你要override的方法太多了。实际上只是用到了ObjectToJson和JsonToObject,对不?

对  
5 楼 lucifer833 2012-04-25  
你要override的方法太多了。实际上只是用到了ObjectToJson和JsonToObject,对不?
4 楼 lucifer833 2012-04-25  
为什么不写一个可以把Model转换成json的方法,然后存到数据库中,使用时再转回来的方法,反而要自定一个JsonType类型呢?楼主,探讨一下。给个原因~~~
3 楼 jinnianshilongnian 2012-04-25  
wanglu271991027 写道
没有看明白  能再详细一点吗

这个 非常简单 就是:

数据库(JSON字符串) 转换为 应用中的Model
2 楼 wanglu271991027 2012-04-25  
没有看明白  能再详细一点吗
1 楼 TJYCHYANGCHENHUI 2012-04-25  
群主写的太好了,学习了!

相关推荐

Global site tag (gtag.js) - Google Analytics