之前在项目中已经决定使用mongoDB+morphia,这种支持复杂结构的数据库的支持,使得我们完全摆脱DB的束缚,完全进行DDD设计,这样,我们游戏中的所有model、DTO以及数据持久对象均采用相同的Class进行表述,而通过丰富的注解(morphia和我们自己的定义的DTO以及其他注解)来控制Model属性所属域。这么做简直太完美了,除了维护逻辑处理的service外,我们几乎只需要维护一个model工程就可以了。
但是,噩耗来了。。。由于项目是与小日本的公司合作开发,游戏要挂在人家的平台上,上周经过多次会议讨论后,最终集团CTO决定使用mysql,因为小日本认为mongoDB不够稳定(擦,那么多大型应用在使用,你丫也太保守了。。。)。没办法,毕竟CTO不愿冒险,理解。。。于是乎放弃使用mongoDB转战hibernate。
由于框架已经形成,AOP编程已经在框架中大行其道,最小的改动就是只换掉morphia,不能让关系型数据库束缚住model的设计,于是自然而然想到hibernate的注解。
经过一天的折腾之后,对注解方式有了一定的了解,总体来讲hibernate的注解基本可以完全替换HBM方式的配置文件,而且易于程序阅读,加上我们之前自己的注解,OK,一切可行。不过,没了mongoDB的sharding和高性能,mysql的sharding实在没法摆脱逻辑的相关性,悲催啊。。。
以前用过hibernate,不过只简单的当做数据库访问和映射工具用,没有实际深究,这次项目比较大,且需要在5个项目上同时开工的架构不能大意,于是稍微深入了下。下面简单列下,hibernate中发现了一些原来我没有想到或注意的问题:
1、主键通过@GeneratedValue进行生成,默认为auto的情况下,如果数据使用mysql,那么主键将会使用数据库的自增方式,这样会出现一个问题:
Session session = sessionFactory.openSession();
Cat mother = new Cat();
mother.setSex("female");
mother.setBirthdate(new Date());
mother.setColor(ColorType.COLOR);
mother.setLitterId(-1);
mother.setWeight(new BigDecimal(123.0D));
session.save(mother);
for (int i = 0; i < 10; i++) {
Cat cat = new Cat();
cat.setBirthdate(new Date());
cat.setLitterId(i);
cat.setMother(mother);
cat.setSex("male");
session.save(cat);
}
System.out.println(session.getFlushMode());
session.flush();
session.close();
在debug上面一段代码(注意代码的数据操作为非事务操作)的时候发现,无论flush是否被调用,只要save执行完成,数据库中就可以看到save的结果已经被同步到数据库,但是按照hibernate官方文档中看,只有session.flush()才执行session的缓存(一级缓存)与数据库同步操作(也就是sql),那问题到底出在哪儿呢?于是,我首先想到了生成主键的问题,因为使用了数据库的生成主键,大家知道,mysql生成的主键的读取是通过一个mysql提供的sql读取的,可以理解为读取上一次自增ID的值,这样,意味着必须insert之后才能拿到自增ID,所以save的同步也就不得不执行了。当然以上只是我的猜想,为了证明,我将cat对象的主键生成方式使用UUIDHexGenerator(在应用本地通过算法计算出一个值),然后再次调用上面的代码,结果证明,只要不进行flush操作,数据库里没有任何结果,而进行flush的时候,通过log可以看到,hibernate进行了一个batch操作,整个batch使用了相同的prepareStatement,供执行了11条sql,也就是说以上的代码中只发送一次sql语句集合给mysql。
由此,我建议大家,如果使用hibernate+mysql,且你的应用在单次逻辑使用了大量的更新操作的时候,且很多sql结构一致的情况下,建议不要使用数据库的自增ID,使用UUID没什么不好,只是稍微长了那么点,哈哈。。。
2、再来说第二个问题,前段时间面试一个开发人员的时候,对方提到了spring集成hibernate的时候注意OpenSessionInViewInterceptor的使用,他认为OpenSessionInViewInterceptor在client用户或server服务器的网络环境不好的情况下使用会导致严重性能问题。
下面让我们分析下,这种说法靠谱不。。。
先说OpenSessionInViewInterceptor的实现吧,顾名思义,它是个拦截器,这个拦截器一般在spring mvc中使用,对应的还有个filter实现我想是给那些不适用spring mvc的框架能够在容器层配置使用吧。那么这个拦截器的拦截点在哪儿呢,经查看源码,其为WebRequestInterceptor的实现,也就是说,是拦截的用户请求开始和处理结束,也就是一次用户请求的生命周期,查看源码后发现,其通过threadlocal保证了整个request的逻辑处理周期中均使用相同的hibernate session,也就是说使用一个jdbc connection,起初在我看来,似乎没有什么问题,尽可能对用户的一次逻辑处理中不频繁的获取连接,即使是连接池,频繁获取也有同步问题,但仔细思考之后,个人认为上文提到的网络环境差的情况下存在性能问题的确会存在。大家都知道,最终用户的request的处理结果会通过一个outpustream流返给用户,而这个流谁提供的实现的呢,那肯定是你的server容器,前文提到,OpenSessionInViewInterceptor最终在request处理结束后进行session的释放,而request的结束标识就是将处理结果输入到output流中,而output流最终会把结果存放到你的server容器的缓冲区中,容器的缓冲区会将结果逐渐发送给OS的缓冲区最终经网络缓冲区发送给用户。基于这样一个过程,问题就来了,当网络环境不好的情况下,如果server内的所有用户的处理结果result都放入缓冲区而没有来得及(因为网络太差了)通过网络返回给用户,这时候很容易造成server容器的缓冲区满,缓冲区满了之后,result通过output流输出的之后就会堵塞,因为满了你塞不进去结果了,如果堵塞就会导致hibernate session没有被释放(因为还没调用到OpenSessionInViewInterceptor的使用session操作),这样,大量的session被占用,导致连接池很快被用光,数据库无法被继续访问,性能问题自然出现。说的有点乱,咱在理一理哈,
网络延迟->网络数据堆积->OS缓冲区满->server容器缓冲区满->向响应的outputstream写入result数据时堵塞->堵塞造成一次request的处理无法完成->无法完成导致OpenSessionInViewInterceptor的释放session操作无法被执行->导致session被很多堵塞线程持有->导致数据库连接池用光->导致无法继续为用户提供服务以及性能问题
分享到:
相关推荐
在Hibernate中,我们需要创建一个`hibernate.cfg.xml`配置文件来设置数据库连接信息,以及`hibernate.hbm.xml`或使用注解的方式来定义对象与数据库表的映射关系。实体类通常包含属性,这些属性对应于数据库表的列,...
在基于注解的 HelloWorld 例子中,我们首先创建了一个名为 Teacher 的 Java 类,该类使用 Hibernate 的注解来定义与数据库表的映射关系。然后,我们配置了 Hibernate 的连接设置,并使用 Hibernate 的 API 实现了对 ...
3. **映射文件(Mapping File)**: Hibernate使用XML或注解来定义实体类与数据库表之间的映射关系。映射文件描述了实体类的属性如何映射到数据库表的列。 4. **会话工厂(SessionFactory)**: 会话工厂是创建会话的...
2. **映射方式**:XML映射文件(hibernate.cfg.xml和.hbm.xml)和注解映射的使用,对比两者的特点和适用场景。 3. **实体类映射**:如何定义实体类,以及如何将类的属性与数据库字段对应,包括基本类型、复杂类型...
本心得旨在分享对Hibernate深入理解的关键点,希望对你在使用Hibernate进行开发时能有所帮助。 一、Hibernate概述 Hibernate是一个开源的ORM框架,它允许我们将Java类与数据库表进行映射,通过对象的方式来操作...
在本文中,我们将重点讨论通过注解方式使用Hibernate的心得体会。 在使用Hibernate进行开发时,注解是必不可少的一部分。`@Entity`注解用于标记一个Java类为实体类,这意味着该类将与数据库中的一个表相对应。如果...
Hibernate通过XML配置文件或注解来定义对象与表之间的映射关系,提供了透明的数据持久化服务。 在学习Hibernate的过程中,需要注意以下几点: 1. Hibernate产生的原因:Hibernate的出现主要是为了解决JDBC编程中的...
- **社区论坛**:用户可以在论坛中交流心得、解决问题。 - **示例项目**:官方提供的示例项目有助于理解如何使用这些工具。 #### 二、下载与安装 **Hibernate Tools**的安装主要分为以下几个步骤: ##### 2.1 ...
项目可能包含实体类(如Song、Artist、Album等),这些类对应数据库中的表,并通过Hibernate的注解或XML配置文件进行映射。 **在线音乐系统功能实现** 1. **音乐播放**:系统可能通过HTTP流媒体技术实现在线播放...
4. DAO层使用Hibernate提供的API执行数据库操作,如Session的save()方法实现新增,update()方法实现更新,delete()方法实现删除,以及find()方法实现查询。 5. Service方法处理完业务逻辑后,返回结果给Controller。...
1. **Clob类型映射**:在Hibernate的实体类中,我们需要声明一个字段来表示Clob类型的属性,并使用`@Lob`注解进行映射。例如: ```java @Lob private Clob largeText; ``` 2. **Hibernate配置**:在Hibernate的...
但现代Hibernate更多使用注解来替代XML映射。 5. **Session接口**:Hibernate的核心接口之一,用于在应用程序和数据库之间进行交互,提供了增删查改(CRUD)的方法。 6. **事务管理**:在Hibernate中,可以通过...
本文旨在总结作者在使用Hibernate过程中的实践经验,并分享一些技巧和心得,希望能够对初次接触或正在学习Hibernate的开发者有所帮助。 #### 二、基础知识 ##### 2.1 库文件配置 在使用Hibernate之前,首先需要...
本篇将详细探讨“Hibernate插入数据”的相关知识点,结合学习心得,深入理解并掌握其核心原理与实践技巧。 首先,Hibernate通过对象关系映射(ORM)技术,将数据库表与Java类关联起来,使得数据库操作可以通过对象...
Spring框架是这个组合的核心,它提供了全面的架构支持,包括IoC(Inverse of Control,控制反转)和DI(Dependency Injection,依赖注入),使得对象之间的依赖关系可以通过配置文件或注解进行管理,降低了组件间的...
这篇文章的作者分享了他在学习Hibernate过程中的心得,虽然描述部分为空,但从给出的博文链接我们可以推测,作者可能深入解析了Hibernate框架的使用,包括其核心概念、配置、对象关系映射(ORM)以及如何在实际项目...
虽然描述为空,但通过提供的博客链接可以推测,这篇笔记可能在该博客中详细解释了作者在实际项目中使用Hibernate的经验和心得,包括可能遇到的问题、解决方案以及最佳实践。访问博客链接可以获取更具体的信息,如...