Hibernate使用及源码分析(一)

Hibernate使用及源码分析(一)

本篇文章主要通过hibernate初级使用分析一下源码,只是给初学者一点小小的建议,不喜勿喷,谢谢!

  • hibernate环境搭建
  • 简单使用
  • 源码走读

一 hibernate环境搭建

这里直接介绍使用maven搭建

首先引入maven相关依赖

<dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
        <!-- 添加Hibernate依赖 -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>3.6.10.Final</version>
        </dependency>

        <!-- 添加Log4J依赖 -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.16</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-nop</artifactId>
            <version>1.6.4</version>
        </dependency>

        <!-- 添加javassist -->
        <dependency>
            <groupId>javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.12.0.GA</version>
        </dependency>

        <!-- mysql数据库的驱动包 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
    </dependencies>

二 简单使用

配置hibernate主配置文件

<?xml version='1.0' encoding='utf-8'?>

<!--表明解析本XML文件的DTD文档位置,DTD是DocumentType Definition 的缩写,

即文档类型的定义,XML解析器使用DTD文档来检查XML文件的合法性。

hibernate.sourceforge.net/hibernate-configuration-3.0dtd可以在

Hibernate3.1.3软件包中的src\org\hibernate目录中找到此文件-->

<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <!--表明以下的配置是针对session-factory配置的,SessionFactory是  

    Hibernate中的一个类,这个类主要负责保存HIbernate的配置信息,以及对Session的

    操作-->

    <session-factory>

        <!--配置数据库的驱动程序,Hibernate在连接数据库时,需要用到数据库的驱  
         动程序-->
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>

        <!--设置数据库的连接url:jdbc:mysql://localhost/hibernate,其中localhost表示mysql服务器名称,此处为本机,hibernate是数据库名-->

        <property name="hibernate.connection.url">jdbc:mysql://localhost/hibernate</property>


        <!--连接数据库是用户名-->

        <property name="hibernate.connection.username">root</property>

        <!--连接数据库是密码-->

        <property name="hibernate.connection.password">root</property>

        <!--数据库连接池的大小-->

        <property name="hibernate.connection.pool.size">20</property>

        <!--是否在后台显示Hibernate用到的SQL语句,开发时设置为true,便于差错,程序运行时可以在Eclipse的控制台显示Hibernate的执行Sql语句。项目部署后可

         以设置为false,提高运行效率-->

        <property name="show_sql">true</property>


        <!--jdbc.fetch_size是指Hibernate每次从数据库中取出并放到JDBC的Statement中的记录条数。FetchSize设的越大,读数据库的次数越少,
        速度越快,Fetch Size越小,读数据库的次数越多,速度越慢-->

        <property name="jdbc.fetch_size">50</property>


        <!--jdbc.batch_size是指Hibernate批量插入,删除和更新时每次操作的记录数。BatchSize越大,
        批量操作的向数据库发送Sql的次数越少,速度就越快,同样耗用内存就越大-->

        <property name="jdbc.batch_size">23</property>


        <!--jdbc.use_scrollable_resultset是否允许Hibernate用JDBC的可滚动的结果集。对分页的结果集。对分页时的设置非常有帮助-->

        <property name="jdbc.use_scrollable_resultset">false</property>


        <!--connection.useUnicode连接数据库时是否使用Unicode编码-->

        <!--<property name="Connection.useUnicode">true</property>-->


        <!--connection.characterEncoding连接数据库时数据的传输字符集编码方式,最好设置为gbk,用gb2312有的字符不全-->

        <!--<property name="connection.characterEncoding">gbk</property>-->


        <!--hibernate.dialect 只是Hibernate使用的数据库方言,就是要用Hibernate连接那种类型的数据库服务器。-->

        <property name="hibernate.dialect">
            org.hibernate.dialect.MySQLDialect
        </property>

        <!--是否自动创建数据库表 他主要有一下几个值:

        validate:当sessionFactory创建时,自动验证或者schema定义导入数据库。

        create:每次启动都drop掉原来的schema,创建新的。

        create-drop:当sessionFactory明确关闭时,drop掉schema。

        update(常用):如果没有schema就创建,有就更新。

        -->

        <property name="hbm2ddl.auto">update</property>


        <!--配置此处 sessionFactory.getCurrentSession()可以完成一系列的工作,当调用时,
        hibernate将session绑定到当前线程,事务结束后,hibernate
        将session从当前线程中释放,并且关闭session。当再次调用getCurrentSession
        ()时,将得到一个新的session,并重新开始这一系列工作。-->
        <property name="current_session_context_class">thread</property>

        <!--<property name="hibernate.generate_statistics">true</property>-->

        <!--<property name="hibernate.connection.autocommit">true</property>-->

        <mapping resource="hibernate-hbm/User.hbm.xml"/>

    </session-factory>
</hibernate-configuration>

创建一个实体类

public class User {
    public static final String TABLE_NAME = "t_user";
    private Long id;
    private String name;
    private Integer age;
    public User() {
    }
    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
}

然后给实体类写一个ORM映射文件

<!-- 一样,这个都可以在hibernate-core包里面找到dtd头部约束 -->
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.test.entity">
    <class name="User" table="t_user">
        <id name="id">
            <generator class="native"/>
        </id>
        <property name="name" column="t_name" type="java.lang.String"/>
        <property name="age" column="t_age" type="java.lang.Integer"/>
    </class>
</hibernate-mapping>

最后开始测试,这里就使用了最简单的save方法来方便阅读源码

    @Test
    public void testSave() throws Exception {
        Configuration configuration = new Configuration().configure();
        SessionFactory factory = configuration.buildSessionFactory();
        Session session = factory.openSession();
        Transaction transaction = session.beginTransaction();
        session.save(new User("里斯", 18));
        transaction.commit();
        session.close();
        factory.close();
    }

三 根据测试方法阅读源码。

1,创建Configuration对象

其中new Configuration()方法主要是初始化Configuration对象中的一些成员变量。

这里写图片描述

这上面一个很重要的类Environment里面都是我们在主配置文件里面配置的属性的key值。
然后通过configure来读取配置文件。
这里写图片描述
这里写图片描述
sax解析配置文件。
这里写图片描述
把session-factory节点里面的属性添加给Configuration
这里写图片描述

通过addProperties()方法将配置文件中的property节点的属性都加载到properties集合中。
从这个方法也可以看出来我们即使不加hibernate前缀也没有问题。
这里写图片描述
通过parseSessionFactory()方法来读取配置文件中的mapping映射文件等等。
当然还有监听器,event等等,后面再分析。
这里写图片描述
从读取映射文件的方法可以看出,我们是有多种配置映射文件的方式的。
这里写图片描述

2.buildSessionFactory()创建session工厂
这里主要是创建了一个SessionFactoryImpl对象。
在他的构造方法里面会进行建表。

Map<String,ClassMetadata> classMeta = new HashMap<String,ClassMetadata>();
classes = cfg.getClassMappings();
while ( classes.hasNext() ) {
   final PersistentClass model = (PersistentClass) classes.next();
   //根据mapping拼接建表语句
   model.prepareTemporaryTables( mapping, settings.getDialect() );
   final String cacheRegionName = cacheRegionPrefix + model.getRootClass().getCacheRegionName();
   // cache region is defined by the root-class in the hierarchy...
   EntityRegionAccessStrategy accessStrategy = ( EntityRegionAccessStrategy ) entityAccessStrategies.get( cacheRegionName );
   if ( accessStrategy == null && settings.isSecondLevelCacheEnabled() ) {
      final AccessType accessType = AccessType.parse( model.getCacheConcurrencyStrategy() );
      if ( accessType != null ) {
         log.trace( "Building cache for entity data [" + model.getEntityName() + "]" );
         EntityRegion entityRegion = settings.getRegionFactory().buildEntityRegion( cacheRegionName, properties, CacheDataDescriptionImpl.decode( model ) );
         accessStrategy = entityRegion.buildAccessStrategy( accessType );
         entityAccessStrategies.put( cacheRegionName, accessStrategy );
         allCacheRegions.put( cacheRegionName, entityRegion );
      }
   }
   。。。。
   //这些就是对应我们在配置文件中配置的建表方式
   if ( settings.isAutoCreateSchema() ) {
   new SchemaExport( cfg, settings ).create( false, true );
}
//创建表
if ( settings.isAutoUpdateSchema() ) {
   new SchemaUpdate( cfg, settings ).execute( false, true );
}
if ( settings.isAutoValidateSchema() ) {
   new SchemaValidator( cfg, settings ).validate();
}
if ( settings.isAutoDropSchema() ) {
   schemaExport = new SchemaExport( cfg, settings );
}

3 打开事务

public Transaction beginTransaction() throws HibernateException {
   errorIfClosed();
   if ( rootSession != null ) {
      // todo : should seriously consider not allowing a txn to begin from a child session
      //      can always route the request to the root session...
      log.warn( "Transaction started on non-root session" );
   }
   Transaction result = getTransaction();
   result.begin();
   return result;
}

4 save()方法
这里写图片描述

save方法会创建一个saveorupdate对象,通过saveeventlistener回调执行保存操作。
也就是调用DefaultSaveOrUpdateEventListener的onSaveOrUpdate()方法。
public void onSaveOrUpdate(SaveOrUpdateEvent event) {
   final SessionImplementor source = event.getSession();
   final Object object = event.getObject();
   final Serializable requestedId = event.getRequestedId();
   if ( reassociateIfUninitializedProxy( object, source ) ) {
      log.trace( "reassociated uninitialized proxy" );
      // an uninitialized proxy, noop, don't even need to
      // return an id, since it is never a save()
   }
   else {
      //initialize properties of the event:
      final Object entity = source.getPersistenceContext().unproxyAndReassociate( object );
      event.setEntity( entity );
      event.setEntry( source.getPersistenceContext().getEntry( entity ) );
      //return the id in the event object
      event.setResultId( performSaveOrUpdate( event ) );
   }
}
最后调用到AbstractSaveEventListener.java的performSaveOrReplicate方法进行insert语句
if ( useIdentityColumn ) {
   EntityIdentityInsertAction insert = new EntityIdentityInsertAction(
         values, entity, persister, source, shouldDelayIdentityInserts
   );

   if ( !shouldDelayIdentityInserts ) {
      log.debug( "executing identity-insert immediately" );
      source.getActionQueue().execute( insert );
      id = insert.getGeneratedId();
      key = new EntityKey( id, persister, source.getEntityMode() );
      source.getPersistenceContext().checkUniqueness( key, entity );
   }
   else {
      log.debug( "delaying identity-insert due to no transaction in progress" );

      source.getActionQueue().addAction( insert );
      key = insert.getDelayedEntityKey();
   }
}

5 最后提交事务

6 关于hibernate不开启事务提交就无法保存等操作的原因分析
还是使用上面save的案例。当我们执行save的时候,AbstractSaveEventListener在调用source.getActionQueue().execute( insert )方法的时候最终调用到AbstractBatcher的prepareStatement方法。

return getPreparedStatement(
                connectionManager.getConnection(),
                sql,
                false,
                getGeneratedKeys,
                null,
                null,
                false
        );

这个里面会通过ConnectionManager获取connection。

    /**
     * Pysically opens a JDBC Connection.
     *
     * @throws HibernateException
     */
    private void openConnection() throws HibernateException {
        if ( connection != null ) {
            return;
        }

        log.debug("opening JDBC connection");
        try {
            connection = factory.getConnectionProvider().getConnection();
        }
        catch (SQLException sqle) {
            throw JDBCExceptionHelper.convert(
                    factory.getSQLExceptionConverter(),
                    sqle,
                    "Cannot open connection"
                );
        }

        callback.connectionOpened(); // register synch; stats.connect()
    }

上面最终会调用到DriverManagerConnectionProvider里面来获取Connection对象。

public Connection getConnection() throws SQLException {

        if ( log.isTraceEnabled() ) log.trace( "total checked-out connections: " + checkedOut );

        synchronized (pool) {
            if ( !pool.isEmpty() ) {
                int last = pool.size() - 1;
                if ( log.isTraceEnabled() ) {
                    log.trace("using pooled JDBC connection, pool size: " + last);
                    checkedOut++;
                }
                Connection pooled = (Connection) pool.remove(last);
                if (isolation!=null) pooled.setTransactionIsolation( isolation.intValue() );
                if ( pooled.getAutoCommit()!=autocommit ) pooled.setAutoCommit(autocommit);
                return pooled;
            }
        }

        log.debug("opening new JDBC connection");
        Connection conn = DriverManager.getConnection(url, connectionProps);
        if (isolation!=null) conn.setTransactionIsolation( isolation.intValue() );
        if ( conn.getAutoCommit()!=autocommit ) conn.setAutoCommit(autocommit);

        if ( log.isDebugEnabled() ) {
            log.debug( "created connection to: " + url + ", Isolation Level: " + conn.getTransactionIsolation() );
        }
        if ( log.isTraceEnabled() ) checkedOut++;
        return conn;
    }

我们可以看到这里面会通过setAutoCommint()方法设置事务提交。所以我们需要查看这个autocommit是哪里控制的。最后我们发现是在DriverManagerConnectionProvider的configure(Properties props)方法里面进行赋值的

 autocommit = PropertiesHelper.getBoolean(Environment.AUTOCOMMIT, props);

    去Environment里面查看这个常量
     /**
     * JDBC autocommit mode
     */
    public static final String AUTOCOMMIT ="hibernate.connection.autocommit";

发现只有配置了hibernate.connection.autocommit为true,hibernate才会自动提交,否则默认是不会提交事务的,必须要我们自己手动提交事务。

好了,今天就到这里,谢谢大家!!!

  • 4
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本下载包与书籍的实战篇紧密结合. 下载包中的程序是一个简明的示例论坛程序,按照循序渐进的原则,分为不同的步骤: 步骤1: 建立最简单的项目基础结构:Board/User/Article三个类,包含最基本的配置文件。同时,在这一步中,建立测试类,刻画项目各个类之间的契约关系。 步骤2: 对文章的种类增加“投票”类型,展示对多型的处理。 步骤3: 增加webwork实现的GUI。 加入中文支持。 步骤4: 升级到Hibernate 3。 环境说明 --------- 程序包中的程序经过测试的运行环境为: 1, Tomcat 5.0.25 2, Hibernate 2.1.7 3, Hibernate 3.0.2 4, Eclipse 3.0.1 (作为IDE) 5, ant 1.6(作为build工具) 6, java sdk 1.4.2 7, mysql 4.1 (作为后台数据库) 8, 至少250M硬盘空间(hibernate 2,3和webwork就将占据138M) 虽然在更高版本中应该可以不加修改的运行,但我们对此不加以保证。(v1.2将对Hibernate 3.0.4进行测试) 安装说明 --------- 1, 确认jdk 1.4.2已经正确安装; 2,将本程序包解压,假设目录为: x:\rs\hib-samples 3, 从hibernate的sf下载区下载2.1.7与3.0.2版本: http://sourceforge.net/project/showfiles.php?group_id=40712 4,将下载的hibernate包解压到x:\rs\hib-samples\hibernate-2.1与x:\rs\hib-samples\hibernate-3,目录结构应该为: x: + rs + hib-samples <--- 本文件包解压的目录 + forum-step1-db-first-middlegen + forum-step1-db-first-synchronizer + forum-step2 + ... + hibernate-2.1 <--- Hibernate 2.1.7解压到这里 + bin <--- 确认bin与doc目录就在解压后的hibernate-2.1目录中 + doc + ... + hibernate-3.0 <--- Hibernate 3.0.2解压到这里 + bin preference -> Java -> Build Path -> User Librarys,分别建立名为Hibernate2,hibernate3,mysql-jdbc的用户库,分别包含对应的lib文件(如hibernate2包含hibernate-2.1目录下的hibernate2.jar及其lib目录下的所有jar文件); 11, 在您的eclipse 中,导入各个目录下的.project文件,以建立工程.(工程名为step1,step2,step3,step4)。在工程的类路径配置中,使用上一步创建的用户库(step1-step3使用Hibernate2,step4使用hibernate3)。确保没有编译错误。 好了,您现在可以开始阅读代码,作一些自己的改变,看看您能得到什么结果 :) 中文处理说明 ------------ 关于中文的处理,是在step3加入的,因此在step1以及step2中,请使用英文进行实验。具体进行的处理为全程采用UTF-8编码. 1,mysql创建时,字符集必须选择UTF-8 2,在mysql jdbc连接的url中,必须指定采用utf-8 encoding。 jdbc:mysql://localhost/forum?useUnicode=true&characterEncoding=utf-8&mysqlEncoding=utf8 3,在jsp页面中,指定页面采用UTF-8编码. 假若在您的项目中,必须使用GBK编码,则在以上的各个地方,都需要把UTF-8更换为GBK方可正常使用。 版本更新 ----------- v1.2 (PLAN,TBD) * 转换到JTA * 给出一个脱离DAO模式的例子 * 给出for hibernate 3的xdoclet例子 v1.1 (2005.5.25) * 分离db目录下的build.xml * 增加了对投票的图形显示 * 修正了和webwork相关的一个中文bug v1.0 (2005.4.20) * 初始版本
Spring和Hibernate是Java开发中非常常用的两个框架,下面简单介绍一下它们的源码分析。 1. Spring源码分析 Spring是一个轻量级的IoC和AOP框架,其中IoC主要是通过BeanFactory和ApplicationContext来实现的。在Spring的源码中,可以看到很多设计模式的运用,如工厂模式、单例模式、策略模式、代理模式等等,这些设计模式的运用使得Spring的代码更加灵活和易于维护。 其中BeanFactory是Spring的核心接口,它提供了IoC容器的基本实现。ApplicationContext是BeanFactory的一个子类,它提供了更多的企业级特性,如AOP、事件传递等等。在Spring的源码中,可以看到很多关于BeanFactory和ApplicationContext的实现,特别是在Bean的生命周期、依赖注入等方面,Spring的源码展示了很多实现细节。 此外,在Spring的源码中,还有很多关于AOP的实现,如代理模式、动态代理、CGLIB等等。这些实现使得Spring能够很好地支持AOP的实现,从而实现了很多企业级应用的需求。 总之,Spring的源码分析可以让我们更好地了解Spring的实现细节,并且对于我们学习和使用Spring也有很大的帮助。 2. Hibernate源码分析 Hibernate是一个Java持久化框架,它提供了对象/关系映射和查询语言等功能。在Hibernate源码中,可以看到很多关于ORM的实现,如映射文件、Session、Transaction等等。 Hibernate的核心接口是SessionFactory和Session,它们是Hibernate的核心实现。在Hibernate源码中,可以看到很多关于Session和Transaction的实现,如缓存机制、事务管理等等。此外,在Hibernate源码中,还有很多关于查询语言的实现,如HQL、Criteria等等。 总之,Hibernate源码分析可以让我们更好地了解Hibernate的实现细节,并且对于我们学习和使用Hibernate也有很大的帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值