zoukankan      html  css  js  c++  java
  • 【迷你微信】基于MINA、Hibernate、Spring、Protobuf的即时聊天系统:5.技术简介之Hibernate

    欢迎阅读我的开源项目《迷你微信》服务器《迷你微信》客户端

    Hibernate是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。 Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序使用,也可以在Servlet/JSP的Web应用中使用,最具革命意义的是,Hibernate可以在应用EJB的J2EE架构中取代CMP,完成数据持久化的重任。——百度百科

    序言

    博主在《迷你微信》项目上对Hibernate的使用是通过注解方式配置进行增删改查操作、使用c3p0连接池、使用ehcache缓存。

    配置

    hibernate.cfg.xml配置文件

    主要是有关和MySQL数据库连接的配置、Ehcache、C3P0的配置

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
    <hibernate-configuration>
    	<session-factory>
    		<!-- Database connection settings -->
    		<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
    		<property name="connection.url">jdbc:mysql://127.0.0.1:3306/MiniWechat?useUnicode=true&amp;characterEncoding=UTF-8</property>
    		<property name="connection.username">root</property>
    		<property name="connection.password">root</property>
    		<property name="javax.persistence.validation.mode">none</property>
    		<!-- SQL dialect -->
    		<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
    		<!-- Enable Hibernate's automatic session context management -->
    		<property name="current_session_context_class">thread</property>
    		<!-- C3P0连接池 -->
    		<property name="c3p0.acquire_increment">1</property>
            <property name="c3p0.idle_test_period">100</property>
            <property name="c3p0.max_size">5</property>
    		<property name="c3p0.max_statements">0</property>
            <property name="c3p0.min_size">2</property>
            <property name="c3p0.timeout">90</property>
            <property name="c3p0.preferredTestQuery ">SELECT CURRENT_USER</property>
            <property name="c3p0.idleConnectionTestPeriod ">18000</property>           
    		<property name="c3p0.maxIdleTime">25000</property>        
    		<property name="c3p0.testConnectionOnCheckout">true</property>	
    		<!-- Echo all executed SQL to stdout -->
    		<property name="show_sql">true</property>
    		<!-- Ehcache 缓存配置 -->
            <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
            <property name="hibernate.cache.use_second_level_cache">true</property>  
    		<property name="hibernate.cache.use_query_cache">true</property>
    		
    		<mapping class="model.User"/>
    		<mapping class="model.Chatting"/>
    		<mapping class="model.Group"/>
    	</session-factory>
    
    </hibernate-configuration>
    

    加载hibernate.cfg.html配置文件并获取Session

    public class HibernateSessionFactory {
    	private static String CONFIG_FILE_LOCATION = "/hibernate.cfg.xml";
    	private static final ThreadLocal<Session> threadLocal = new ThreadLocal<Session>();
    	private static Configuration configuration = new Configuration();
    	public static SessionFactory sessionFactory;
    	private static String configFile = CONFIG_FILE_LOCATION;
    
    	static {
    		try{
    			configuration.configure(configFile);
    			ServiceRegistry sr = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
    			sessionFactory = configuration.buildSessionFactory(sr);
    		}catch(Exception e){
    			e.printStackTrace();
    		}
    	}
    	private HibernateSessionFactory() {
    	}
    	public static Session getSession() {
    		Session session = threadLocal.get();
    		if (session == null || !session.isOpen()) {
    			session = (sessionFactory != null) ? sessionFactory.openSession() : null;
    			threadLocal.set(session);
    		}
    		return session;
    	}
    }
    

    对象的注解配置

    主要是类的注解配置、成员变量的注解配置、多对多的注解配置
    1.在需要在数据库中建表存储的类型需要在类名前添加注解

    @Entity
    @Table(name="user")
    

    2.在相关成员变量添加注解

     @Column(name="user_id",columnDefinition = "char(20)  COMMENT '微信号'")
    

    如果是数据库对应的主键需要添加注解:

    @Id
    

    如果是自增主键需要添加主键:

    @GeneratedValue(strategy = GenerationType.AUTO)
    

    3.多对多关联注解配置
    在数据库中我们需要好友关系、聊天群的成员表,但在项目代码中我们并没有定义这样的对象,这是通过多对多的注解配置实现的
    例如:

    @ManyToMany(targetEntity=User.class)
       @JoinTable(name = "user_friends", 
       joinColumns = @JoinColumn(name = "user_id"), 	
       inverseJoinColumns = @JoinColumn(name = "friend_id"))
       public List<User> getFriends() {
       	return friends;
       }
       public void setFriends(List<User> friends) {
       	this.friends = friends;
       }
    

    增删改查

    在项目中为了更好的将有关数据库的操作和业务逻辑分离,同时也有利于BUG和异常的捕获,我们将有关数据库的操作进行了封装,下面的代码只展示了有关Hibernate的增删改查操作的代码,没有展示封装的代码。

    具体的增删改查代码

    增:

    public void add(Object obj){
    	Session session = HibernateSessionFactory.getSession();
    	Transaction trans = session.beginTransaction();
    	session.save(obj);
    	trans.commit();
    	session.close();
    }
    

    删:

    public  void delete(Object obj){
    	Session session = HibernateSessionFactory.getSession();
    	Transaction trans = session.beginTransaction();
    	session.delete(o);
    	trans.commit();
    	session.close();
    }
    

    改:

    public  void update(Object obj){
    	Session session = HibernateSessionFactory.getSession();
    	Transaction trans = session.beginTransaction();
    	session.update(o);
    	trans.commit();
    	session.close();
    }
    

    查:

    public  void query(String paramName, Object paramValue, Class outputClass){
    	Session session = HibernateSessionFactory.getSession();
    	Criteria criteria = session.createCriteria(outputClass);
    	// 使用缓存
    	criteria.setCacheable(true);
    	criteria.add(Restrictions.eq(paramName, paramValue));
    	List list = criteria.list();
    	session.close();
    }
    

    注意:
    1.使用了缓存 后面会更加详细的介绍
    2.paramValue的类型需要和查询对象定义的类型相匹配,否则会出现类型无法转换的报错

    数据库操作的封装

    1.将所有的数据库操作放在同一个类中,并声明为静态方法
    2.对所有的操作进行异常捕获 避免抛出数据库、Hibernate相关的异常
    3.定义枚举类型的ResultCode 返回操作是否成功
    4.重要:在项目中 聊天群类型中定义了 聊天成员对象的List,当查询一个聊天群对象时,Hibernate并没有将聊天成员对象的List也查询出来,而是当需要使用的时候再查询,但前面提到的查询方法中调用了session.close(),这时无法再用原来的聊天群对象直接get聊天成员对象的List(Hibernate的延迟加载导致)。所以在封装的数据库操作类中重载了需要使用到延迟加载的相关方法,即:调用相关方法时传入session,在查询等操作时不立即close掉session,待调用者的方法结束时再close。Session是通过sessionFactory.openSession()方法获取,其实通过这种方式获取的ession时Hibernate会自动关闭。

    连接池

    在项目开始时并没有使用连接池,但出现过一次数据库无法访问的异常。经过查阅资料发现了问题所在:Mysql服务器默认的“wait_timeout”是8小时,也就是说一个connection空闲超过8个小时,Mysql将自动断开该 connection。Hibernate默认的连接池并不知道该connection已经失效,如果这时有 Client请求connection,Hibernate将该失效的Connection提供给Client,将会造成上面的异常。
    解决办法:使用C3P0连接池,有关C3P0连接池的配置在hibernate.cfg.xml配置文件中可以查看,需要的jar包可以在Hibernate的安装包可以找到。
    其实一开始使用的是C3P0连接池,使用的是Proxool连接池,但使用过程中遇到了连接池销毁连接时的相关报错,而且没有找到很好的解决办法。所以就改用了C3P0连接池

    缓存

    EhCache是Hibernate的二级缓存技术之一,可以把查询出来的数据存储在内存或者磁盘,节省下次同样查询语句再次查询数据库,大幅减轻数据库压力
    1.有关EhCache的配置可以在hibernat.cfg.xml中查看
    2.在需要使用缓存的类添加注解

    @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
    

    3.在数据库的查询操作中设置使用缓存

    // 使用缓存
    criteria.setCacheable(true);
    

    3.当用Hibernate的方式修改表数据(save,update,delete等等),这时EhCache会自动把缓存中关于此表的所有缓存全部删除掉(这样能达到同步)。但对于数据经常修改的表来说,可能就失去缓存的意义了(不能减轻数据库压力);所以EhCache一般要使用在比较少执行write操作的表(包括update,insert,delete等)[Hibernate的二级缓存也都是这样];

    注:本篇来自于数据库调试员:王飞先生
    欢迎阅读我的开源项目《迷你微信》服务器《迷你微信》客户端

  • 相关阅读:
    【Linux笔记】Linux目录结构
    《Effective C#》快速笔记(五)-
    《Effective C#》快速笔记(四)- 使用框架
    《Effective C#》快速笔记(三)- 使用 C# 表达设计
    《Effective C#》快速笔记(二)- .NET 资源托管
    《Effective C#》快速笔记(一)- C# 语言习惯
    Visual Studio 数据库架构比较
    C# 反射与dynamic最佳组合
    C# 调用WebApi
    基于微软开发平台构建和使用私有NuGet托管库
  • 原文地址:https://www.cnblogs.com/xiaozefeng/p/mina_wechat_Java_5.html
Copyright © 2011-2022 走看看