`
haohao-xuexi02
  • 浏览: 215586 次
  • 性别: Icon_minigender_2
  • 来自: 北京
社区版块
存档分类
最新评论

hibernate与对象共事

阅读更多

Hibernate是完整的对象/关系映射解决方案,它提供了对象状态管理(state management)的功能,使开发者不再需要理会底层数据库系统的细节。 也就是说,相对于常见的JDBC/SQL持久层方案中需要管理SQL语句,Hibernate采用了更自然的面向对象的视角来持久化Java应用中的数据。

换句话说,使用Hibernate的开发者应该总是关注对象的状态(state),不必考虑SQL语句的执行。 这部分细节已经由Hibernate掌管妥当,只有开发者在进行系统性能调优的时候才需要进行了解。

10.1. Hibernate对象状态(object states)

Hibernate定义并支持下列对象状态(state):

  • 瞬时(Transient) - 由new操作符创建,且尚未与Hibernate Session 关联的对象被认定为瞬时(Transient)的。瞬时(Transient)对象不会被持久化到数据库中,也不会被赋予持久化标识(identifier)。 如果瞬时(Transient)对象在程序中没有被引用,它会被垃圾回收器(garbage collector)销毁。 使用Hibernate Session可以将其变为持久(Persistent)状态。(Hibernate会自动执行必要的SQL语句)

  • 持久(Persistent) - 持久(Persistent)的实例在数据库中有对应的记录,并拥有一个持久化标识(identifier)。 持久(Persistent)的实例可能是刚被保存的,或刚被加载的,无论哪一种,按定义,它存在于相关联的Session作用范围内。 Hibernate会检测到处于持久(Persistent)状态的对象的任何改动,在当前操作单元(unit of work)执行完毕时将对象数据(state)与数据库同步(synchronize)。 开发者不需要手动执行UPDATE。将对象从持久(Persistent)状态变成瞬时(Transient)状态同样也不需要手动执行DELETE语句。

  • 脱管(Detached) - 与持久(Persistent)对象关联的Session被关闭后,对象就变为脱管(Detached)的。 对脱管(Detached)对象的引用依然有效,对象可继续被修改。脱管(Detached)对象如果重新关联到某个新的Session上, 会再次转变为持久(Persistent)的(在Detached其间的改动将被持久化到数据库)。 这个功能使得一种编程模型,即中间会给用户思考时间(user think-time)的长时间运行的操作单元(unit of work)的编程模型成为可能。 我们称之为应用程序事务,即从用户观点看是一个操作单元(unit of work)。

接下来我们来细致的讨论下状态(states)及状态间的转换(state transitions)(以及触发状态转换的Hibernate方法)。

10.2. 使对象持久化

Hibernate认为持久化类(persistent class)新实例化的对象是瞬时(Transient)的。 我们可通过将瞬时(Transient)对象与session关联而把它变为持久(Persistent)的。

DomesticCat fritz = new DomesticCat();
fritz.setColor(Color.GINGER);
fritz.setSex('M');
fritz.setName("Fritz");
Long generatedId = (Long) sess.save(fritz);

如果Cat的持久化标识(identifier)是generated类型的, 那么该标识(identifier)会自动在save()被调用时产生并分配给cat。 如果Cat的持久化标识(identifier)是assigned类型的,或是一个复合主键(composite key), 那么该标识(identifier)应当在调用save()之前手动赋予给cat。 你也可以按照EJB3 early draft中定义的语义,使用persist()替代save()

此外,你可以用一个重载版本的save()方法。

DomesticCat pk = new DomesticCat();
pk.setColor(Color.TABBY);
pk.setSex('F');
pk.setName("PK");
pk.setKittens( new HashSet() );
pk.addKitten(fritz);
sess.save( pk, new Long(1234) );

如果你持久化的对象有关联的对象(associated objects)(例如上例中的kittens集合) 那么对这些对象(译注:pk和kittens)进行持久化的顺序是任意的(也就是说可以先对kittens进行持久化也可以先对pk进行持久化), 除非你在外键列上有NOT NULL约束。 Hibernate不会违反外键约束,但是如果你用错误的顺序持久化对象(译注:在pk持久化之前持久化kitten),那么可能会违反NOT NULL约束。

通常你不会为这些细节烦心,因为你很可能会使用Hibernate的 传播性持久化(transitive persistence)功能自动保存相关联那些对象。 这样连违反NOT NULL约束的情况都不会出现了 - Hibernate会管好所有的事情。 传播性持久化(transitive persistence)将在本章稍后讨论。

10.3. 装载对象

如果你知道某个实例的持久化标识(identifier),你就可以使用Sessionload()方法 来获取它。 load()的另一个参数是指定类的.class对象。 本方法会创建指定类的持久化实例,并从数据库加载其数据(state)。

Cat fritz = (Cat) sess.load(Cat.class, generatedId);
// you need to wrap primitive identifiers
long id = 1234;
DomesticCat pk = (DomesticCat) sess.load( DomesticCat.class, new Long(id) );

此外, 你可以把数据(state)加载到指定的对象实例上(覆盖掉该实例原来的数据)。

Cat cat = new DomesticCat();
// load pk's state into cat
sess.load( cat, new Long(pkId) );
Set kittens = cat.getKittens();

请注意如果没有匹配的数据库记录,load()方法可能抛出无法恢复的异常(unrecoverable exception)。 如果类的映射使用了代理(proxy),load()方法会返回一个未初始化的代理,直到你调用该代理的某方法时才会去访问数据库。 若你希望在某对象中创建一个指向另一个对象的关联,又不想在从数据库中装载该对象时同时装载相关联的那个对象,那么这种操作方式就用得上的了。 如果为相应类映射关系设置了batch-size, 那么使用这种操作方式允许多个对象被一批装载(因为返回的是代理,无需从数据库中抓取所有对象的数据)。

如果你不确定是否有匹配的行存在,应该使用get()方法,它会立刻访问数据库,如果没有对应的记录,会返回null。

Cat cat = (Cat) sess.get(Cat.class, id);
if (cat==null) {
    cat = new Cat();
    sess.save(cat, id);
}
return cat;

你甚至可以选用某个LockMode,用SQL的SELECT ... FOR UPDATE装载对象。 请查阅API文档以获取更多信息。

Cat cat = (Cat) sess.get(Cat.class, id, LockMode.UPGRADE);

注意,任何关联的对象或者包含的集合都不会被以FOR UPDATE方式返回, 除非你指定了lock或者all作为关联(association)的级联风格(cascade style)。

任何时候都可以使用refresh()方法强迫装载对象和它的集合。如果你使用数据库触发器功能来处理对象的某些属性,这个方法就很有用了。

sess.save(cat);
sess.flush(); //force the SQL INSERT
sess.refresh(cat); //re-read the state (after the trigger executes)

此处通常会出现一个重要问题: Hibernate会从数据库中装载多少东西?会执行多少条相应的SQLSELECT语句? 这取决于抓取策略(fetching strategy),会在第 19.1 节 “ 抓取策略(Fetching strategies) ”中解释。

10.4. 查询

如果不知道所要寻找的对象的持久化标识,那么你需要使用查询。Hibernate支持强大且易于使用的面向对象查询语言(HQL)。 如果希望通过编程的方式创建查询,Hibernate提供了完善的按条件(Query By Criteria, QBC)以及按样例(Query By Example, QBE)进行查询的功能。 你也可以用原生SQL(native SQL)描述查询,Hibernate额外提供了将结果集(result set)转化为对象的支持。

10.4.1. 执行查询

HQL和原生SQL(native SQL)查询要通过为org.hibernate.Query的实例来表达。 这个接口提供了参数绑定、结果集处理以及运行实际查询的方法。 你总是可以通过当前Session获取一个Query对象:

List cats = session.createQuery(
    "from Cat as cat where cat.birthdate < ?")
    .setDate(0, date)
    .list();

List mothers = session.createQuery(
    "select mother from Cat as cat join cat.mother as mother where cat.name = ?")
    .setString(0, name)
    .list();

List kittens = session.createQuery(
    "from Cat as cat where cat.mother = ?")
    .setEntity(0, pk)
    .list();

Cat mother = (Cat) session.createQuery(
    "select cat.mother from Cat as cat where cat = ?")
    .setEntity(0, izi)
    .uniqueResult();]]

Query mothersWithKittens = (Cat) session.createQuery(
    "select mother from Cat as mother left join fetch mother.kittens");
Set uniqueMothers = new HashSet(mothersWithKittens.list());

一个查询通常在调用list()时被执行,执行结果会完全装载进内存中的一个集合(collection)。 查询返回的对象处于持久(persistent)状态。如果你知道的查询只会返回一个对象,可使用list()的快捷方式uniqueResult()。 注意,使用集合预先抓取的查询往往会返回多次根对象(他们的集合类都被初始化了)。你可以通过一个集合来过滤这些重复对象。

10.4.1.1. 迭代式获取结果(Iterating results)

某些情况下,你可以使用iterate()方法得到更好的性能。 这通常是你预期返回的结果在session,或二级缓存(second-level cache)中已经存在时的情况。 如若不然,iterate()会比list()慢,而且可能简单查询也需要进行多次数据库访问: iterate()会首先使用1条语句得到所有对象的持久化标识(identifiers),再根据持久化标识执行n条附加的select语句实例化实际的对象。

// fetch ids
Iterator iter = sess.createQuery("from eg.Qux q order by q.likeliness").iterate();
while ( iter.hasNext() ) {
    Qux qux = (Qux) iter.next();  // fetch the object
    // something we couldnt express in the query
    if ( qux.calculateComplicatedAlgorithm() ) {
        // delete the current instance
        iter.remove();
        // dont need to process the rest
        break;
    }
}

10.4.1.2. 返回元组(tuples)的查询

(译注:元组(tuples)指一条结果行包含多个对象) Hibernate查询有时返回元组(tuples),每个元组(tuples)以数组的形式返回:

Iterator kittensAndMothers = sess.createQuery(
            "select kitten, mother from Cat kitten join kitten.mother mother")
            .list()
            .iterator();

while ( kittensAndMothers.hasNext() ) {
    Object[] tuple = (Object[]) kittensAndMothers.next();
    Cat kitten  = tuple[0];
    Cat mother  = tuple[1];
    ....
}

10.4.1.3. 标量(Scalar)结果

查询可在select从句中指定类的属性,甚至可以调用SQL统计(aggregate)函数。 属性或统计结果被认定为"标量(Scalar)"的结果(而不是持久(persistent state)的实体)。

Iterator results = sess.createQuery(
        "select cat.color, min(cat.birthdate), count(cat) from Cat cat " +
        "group by cat.color")
        .list()
        .iterator();
        
while ( results.hasNext() ) {
    Object[] row = (Object[]) results.next();
    Color type = (Color) row[0];
    Date oldest = (Date) row[1];
    Integer count = (Integer) row[2];
    .....
}

10.4.1.4. 绑定参数

接口Query提供了对命名参数(named parameters)、JDBC风格的问号(?)参数进行绑定的方法。 不同于JDBC,Hibernate对参数从0开始计数。 命名参数(named parameters)在查询字符串中是形如:name的标识符。 命名参数(named parameters)的优点是:

  • 命名参数(named parameters)与其在查询串中出现的顺序无关

  • 它们可在同一查询串中多次出现

  • 它们本身是自我说明的

//named parameter (preferred)
Query q = sess.createQuery("from DomesticCat cat where cat.name = :name");
q.setString("name", "Fritz");
Iterator cats = q.iterate();
//positional parameter
Query q = sess.createQuery("from DomesticCat cat where cat.name = ?");
q.setString(0, "Izi");
Iterator cats = q.iterate();
//named parameter list
List names = new ArrayList();
names.add("Izi");
names.add("Fritz");
Query q = sess.createQuery("from DomesticCat cat where cat.name in (:namesList)");
q.setParameterList("namesList", names);
List cats = q.list();

10.4.1.5. 分页

如果你需要指定结果集的范围(希望返回的最大行数/或开始的行数),应该使用Query接口提供的方法:

Query q = sess.createQuery("from DomesticCat cat");
q.setFirstResult(20);
q.setMaxResults(10);
List cats = q.list();

Hibernate 知道如何将这个有限定条件的查询转换成你的数据库的原生SQL(native SQL)。

10.4.1.6. 可滚动遍历(Scrollable iteration)

如果你的JDBC驱动支持可滚动的ResuleSetQuery接口可以使用ScrollableResults,允许你在查询结果中灵活游走。

Query q = sess.createQuery("select cat.name, cat from DomesticCat cat " +
                            "order by cat.name");
ScrollableResults cats = q.scroll();
if ( cats.first() ) {

    // find the first name on each page of an alphabetical list of cats by name
    firstNamesOfPages = new ArrayList();
    do {
        String name = cats.getString(0);
        firstNamesOfPages.add(name);
    }
    while ( cats.scroll(PAGE_SIZE) );

    // Now get the first page of cats
    pageOfCats = new ArrayList();
    cats.beforeFirst();
    int i=0;
    while( ( PAGE_SIZE > i++ ) && cats.next() ) pageOfCats.add( cats.get(1) );

}
cats.close()

请注意,使用此功能需要保持数据库连接(以及游标(cursor))处于一直打开状态。 如果你需要断开连接使用分页功能,请使用setMaxResult()/setFirstResult()

10.4.1.7. 外置命名查询(Externalizing named queries)

你可以在映射文件中定义命名查询(named queries)。 (如果你的查询串中包含可能被解释为XML标记(markup)的字符,别忘了用CDATA包裹起来。)

<query name="eg.DomesticCat.by.name.and.minimum.weight"><![CDATA[
    from eg.DomesticCat as cat
        where cat.name = ?
        and cat.weight > ?
] ]></query>

参数绑定及执行以编程方式(programatically)完成:

Query q = sess.getNamedQuery("eg.DomesticCat.by.name.and.minimum.weight");
q.setString(0, name);
q.setInt(1, minWeight);
List cats = q.list();

请注意实际的程序代码与所用的查询语言无关,你也可在元数据中定义原生SQL(native SQL)查询, 或将原有的其他的查询语句放在配置文件中,这样就可以让Hibernate统一管理,达到迁移的目的。

10.4.2. 过滤集合

集合过滤器(filter)是一种用于一个持久化集合或者数组的特殊的查询。查询字符串中可以使用"this"来引用集合中的当前元素。

Collection blackKittens = session.createFilter(
    pk.getKittens(), 
    "where this.color = ?")
    .setParameter( Color.BLACK, Hibernate.custom(ColorUserType.class) )
    .list()
);

返回的集合可以被认为是一个包(bag, 无顺序可重复的集合(collection)),它是所给集合的副本。 原来的集合不会被改动(这与“过滤器(filter)”的隐含的含义不符,不过与我们期待的行为一致)。

请注意过滤器(filter)并不需要from子句(当然需要的话它们也可以加上)。过滤器(filter)不限定于只能返回集合元素本身。

Collection blackKittenMates = session.createFilter(
    pk.getKittens(), 
    "select this.mate where this.color = eg.Color.BLACK.intValue")
    .list();

即使无条件的过滤器(filter)也是有意义的。例如,用于加载一个大集合的子集:

Collection tenKittens = session.createFilter(
    mother.getKittens(), "")
    .setFirstResult(0).setMaxResults(10)
    .list();

10.4.3. 条件查询(Criteria queries)

HQL极为强大,但是有些人希望能够动态的使用一种面向对象API创建查询,而非在他们的Java代码中嵌入字符串。对于那部分人来说,Hibernate提供了直观的Criteria查询API。

Criteria crit = session.createCriteria(Cat.class);
crit.add( Expression.eq( "color", eg.Color.BLACK ) );
crit.setMaxResults(10);
List cats = crit.list();

Criteria以及相关的样例(Example)API将会再第 15 章 条件查询(Criteria Queries) 中详细讨论。

10.4.4. 使用原生SQL的查询

你可以使用createSQLQuery()方法,用SQL来描述查询,并由Hibernate将结果集转换成对象。 请注意,你可以在任何时候调用session.connection()来获得并使用JDBC Connection对象。 如果你选择使用Hibernate的API, 你必须把SQL别名用大括号包围起来:

List cats = session.createSQLQuery(
    "SELECT {cat.*} FROM CAT {cat} WHERE ROWNUM<10",
    "cat",
    Cat.class
).list();
List cats = session.createSQLQuery(
    "SELECT {cat}.ID AS {cat.id}, {cat}.SEX AS {cat.sex}, " +
           "{cat}.MATE AS {cat.mate}, {cat}.SUBCLASS AS {cat.class}, ... " +
    "FROM CAT {cat} WHERE ROWNUM<10",
    "cat",
    Cat.class
).list()

和Hibernate查询一样,SQL查询也可以包含命名参数和占位参数。 可以在第 16 章 Native SQL查询找到更多关于Hibernate中原生SQL(native SQL)的信息。

10.5. 修改持久对象

事务中的持久实例(就是通过session装载、保存、创建或者查询出的对象) 被应用程序操作所造成的任何修改都会在Session刷出(flushed)的时候被持久化(本章后面会详细讨论)。 这里不需要调用某个特定的方法(比如update(),设计它的目的是不同的)将你的修改持久化。 所以最直接的更新一个对象的方法就是在Session处于打开状态时load()它,然后直接修改即可:

DomesticCat cat = (DomesticCat) sess.load( Cat.class, new Long(69) );
cat.setName("PK");
sess.flush();  // changes to cat are automatically detected and persisted

有时这种程序模型效率低下,因为它在同一Session里需要一条SQL SELECT语句(用于加载对象) 以及一条SQL UPDATE语句(持久化更新的状态)。 为此Hibernate提供了另一种途径,使用脱管(detached)实例。

请注意Hibernate本身不提供直接执行UPDATEDELETE语句的API。 Hibernate提供的是状态管理(state management)服务,你不必考虑要使用的语句(statements)。 JDBC是出色的执行SQL语句的API,任何时候调用session.connection()你都可以得到一个JDBC Connection对象。 此外,在联机事务处理(OLTP)程序中,大量操作(mass operations)与对象/关系映射的观点是相冲突的。 Hibernate的将来版本可能会提供专门的进行大量操作(mass operation)的功能。 参考第 13 章 批量处理(Batch processing),寻找一些可用的批量(batch)操作技巧。

10.6. 修改脱管(Detached)对象

很多程序需要在某个事务中获取对象,然后将对象发送到界面层去操作,最后在一个新的事务保存所做的修改。 在高并发访问的环境中使用这种方式,通常使用附带版本信息的数据来保证这些“长“工作单元之间的隔离。

Hibernate通过提供Session.update()Session.merge() 重新关联脱管实例的办法来支持这种模型。

// in the first session
Cat cat = (Cat) firstSession.load(Cat.class, catId);
Cat potentialMate = new Cat();
firstSession.save(potentialMate);

// in a higher layer of the application
cat.setMate(potentialMate);

// later, in a new session
secondSession.update(cat);  // update cat
secondSession.update(mate); // update mate

如果具有catId持久化标识的Cat之前已经被另一Session(secondSession)装载了, 应用程序进行重关联操作(reattach)的时候会抛出一个异常。

如果你确定当前session没有包含与之具有相同持久化标识的持久实例,使用update()。 如果想随时合并你的的改动而不考虑session的状态,使用merge()。 换句话说,在一个新session中通常第一个调用的是update()方法,以便保证重新关联脱管(detached)对象的操作首先被执行。

如果希望相关联的脱管对象(通过引用“可到达”的脱管对象)的数据也要更新到数据库时(并且也仅仅在这种情况), 可以对该相关联的脱管对象单独调用update() 当然这些可以自动完成,即通过使用传播性持久化(transitive persistence),请看第 10.11 节 “传播性持久化(transitive persistence)”

lock()方法也允许程序重新关联某个对象到一个新session上。不过,该脱管(detached)的对象必须是没有修改过的!

//just reassociate:
sess.lock(fritz, LockMode.NONE);
//do a version check, then reassociate:
sess.lock(izi, LockMode.READ);
//do a version check, using SELECT ... FOR UPDATE, then reassociate:
sess.lock(pk, LockMode.UPGRADE);

请注意,lock()可以搭配多种LockMode, 更多信息请阅读API文档以及关于事务处理(transaction handling)的章节。重新关联不是lock()的唯一用途。

其他用于长时间工作单元的模型会在第 11.3 节 “乐观并发控制(Optimistic concurrency control)”中讨论。

10.7. 自动状态检测

Hibernate的用户曾要求一个既可自动分配新持久化标识(identifier)保存瞬时(transient)对象,又可更新/重新关联脱管(detached)实例的通用方法。 saveOrUpdate()方法实现了这个功能。

// in the first session
Cat cat = (Cat) firstSession.load(Cat.class, catID);

// in a higher tier of the application
Cat mate = new Cat();
cat.setMate(mate);

// later, in a new session
secondSession.saveOrUpdate(cat);   // update existing state (cat has a non-null id)
secondSession.saveOrUpdate(mate);  // save the new instance (mate has a null id)

saveOrUpdate()用途和语义可能会使新用户感到迷惑。 首先,只要你没有尝试在某个session中使用来自另一session的实例,你就应该不需要使用update()saveOrUpdate(),或merge()。有些程序从来不用这些方法。

通常下面的场景会使用update()saveOrUpdate()

  • 程序在第一个session中加载对象

  • 该对象被传递到表现层

  • 对象发生了一些改动

  • 该对象被返回到业务逻辑层

  • 程序调用第二个session的update()方法持久这些改动

saveOrUpdate()做下面的事:

  • 如果对象已经在本session中持久化了,不做任何事

  • 如果另一个与本session关联的对象拥有相同的持久化标识(identifier),抛出一个异常

  • 如果对象没有持久化标识(identifier)属性,对其调用save()

  • 如果对象的持久标识(identifier)表明其是一个新实例化的对象,对其调用save()

  • 如果对象是附带版本信息的(通过<version><timestamp>) 并且版本属性的值表明其是一个新实例化的对象,save()它。

  • 否则update() 这个对象

merge()可非常不同:

  • 如果session中存在相同持久化标识(identifier)的实例,用用户给出的对象的状态覆盖旧有的持久实例

  • 如果session没有相应的持久实例,则尝试从数据库中加载,或创建新的持久化实例

  • 最后返回该持久实例

  • 用户给出的这个对象没有被关联到session上,它依旧是脱管的

10.8. 删除持久对象

使用Session.delete()会把对象的状态从数据库中移除。 当然,你的应用程序可能仍然持有一个指向已删除对象的引用。所以,最好这样理解:delete()的用途是把一个持久实例变成瞬时(transient)实例。

sess.delete(cat);

你可以用你喜欢的任何顺序删除对象,不用担心外键约束冲突。当然,如果你搞错了顺序,还是有可能引发在外键字段定义的NOT NULL约束冲突。 例如你删除了父对象,但是忘记删除孩子们。

10.9. 在两个不同数据库间复制对象

偶尔会用到不重新生成持久化标识(identifier),将持久实例以及其关联的实例持久到不同的数据库中的操作。

//retrieve a cat from one database
Session session1 = factory1.openSession();
Transaction tx1 = session1.beginTransaction();
Cat cat = session1.get(Cat.class, catId);
tx1.commit();
session1.close();

//reconcile with a second database
Session session2 = factory2.openSession();
Transaction tx2 = session2.beginTransaction();
session2.replicate(cat, ReplicationMode.LATEST_VERSION);
tx2.commit();
session2.close();

ReplicationMode决定在和数据库中已存在记录由冲突时,replicate()如何处理。

  • ReplicationMode.IGNORE - 忽略它

  • ReplicationMode.OVERWRITE - 覆盖相同的行

  • ReplicationMode.EXCEPTION - 抛出异常

  • ReplicationMode.LATEST_VERSION - 如果当前的版本较新,则覆盖,否则忽略

这个功能的用途包括使录入的数据在不同数据库中一致,产品升级时升级系统配置信息,回滚non-ACID事务中的修改等等。 (译注,non-ACID,非ACID;ACID,Atomic,Consistent,Isolated and Durable的缩写)

10.10. Session刷出(flush)

每间隔一段时间,Session会执行一些必需的SQL语句来把内存中的对象的状态同步到JDBC连接中。这个过程被称为刷出(flush),默认会在下面的时间点执行:

  • 在某些查询执行之前

  • 在调用org.hibernate.Transaction.commit()的时候

  • 在调用Session.flush()的时候

涉及的SQL语句会按照下面的顺序发出执行:

  1. 所有对实体进行插入的语句,其顺序按照对象执行Session.save()的时间顺序

  2. 所有对实体进行更新的语句

  3. 所有进行集合删除的语句

  4. 所有对集合元素进行删除,更新或者插入的语句

  5. 所有进行集合插入的语句

  6. 所有对实体进行删除的语句,其顺序按照对象执行Session.delete()的时间顺序

(有一个例外是,如果对象使用native方式来生成ID(持久化标识)的话,它们一执行save就会被插入。)

除非你明确地发出了flush()指令,关于Session何时会执行这些JDBC调用是完全无法保证的,只能保证它们执行的前后顺序。 当然,Hibernate保证,Query.list(..)绝对不会返回已经失效的数据,也不会返回错误数据。

也可以改变默认的设置,来让刷出(flush)操作发生的不那么频繁。 FlushMode类定义了三种不同的方式。 仅在提交时刷出(仅当Hibernate的Transaction API被使用时有效), 按照刚才说的方式刷出, 以及除非明确使用flush()否则从不刷出。 最后一种模式对于那些需要长时间保持Session为打开或者断线状态的长时间运行的工作单元很有用。 (参见 第 11.3.2 节 “扩展周期的session和自动版本化”).

sess = sf.openSession();
Transaction tx = sess.beginTransaction();
sess.setFlushMode(FlushMode.COMMIT); // allow queries to return stale state

Cat izi = (Cat) sess.load(Cat.class, id);
izi.setName(iznizi);

// might return stale data
sess.find("from Cat as cat left outer join cat.kittens kitten");

// change to izi is not flushed!
...
tx.commit(); // flush occurs
sess.close();

刷出(flush)期间,可能会抛出异常。(例如一个DML操作违反了约束) 异常处理涉及到对Hibernate事务性行为的理解,因此我们将在第 11 章 事务和并发中讨论。

10.11. 传播性持久化(transitive persistence)

对每一个对象都要执行保存,删除或重关联操作让人感觉有点麻烦,尤其是在处理许多彼此关联的对象的时候。 一个常见的例子是父子关系。考虑下面的例子:

如果一个父子关系中的子对象是值类型(value typed)(例如,地址或字符串的集合)的,他们的生命周期会依赖于父对象,可以享受方便的级联操作(Cascading),不需要额外的动作。 父对象被保存时,这些值类型(value typed)子对象也将被保存;父对象被删除时,子对象也将被删除。 这对将一个子对象从集合中移除是同样有效:Hibernate会检测到,并且因为值类型(value typed)的对象不可能被其他对象引用,所以Hibernate会在数据库中删除这个子对象。

现在考虑同样的场景,不过父子对象都是实体(entities)类型,而非值类型(value typed)(例如,类别与个体,或母猫和小猫)。 实体有自己的生命期,允许共享对其的引用(因此从集合中移除一个实体,不意味着它可以被删除), 并且实体到其他关联实体之间默认没有级联操作的设置。 Hibernate默认不实现所谓的可到达即持久化(persistence by reachability)的策略。

每个Hibernate session的基本操作 - 包括 persist(), merge(), saveOrUpdate(), delete(), lock(), refresh(), evict(), replicate() - 都有对应的级联风格(cascade style)。 这些级联风格(cascade style)风格分别命名为 create, merge, save-update, delete, lock, refresh, evict, replicate。 如果你希望一个操作被顺着关联关系级联传播,你必须在映射文件中指出这一点。例如:

<one-to-one name="person" cascade="persist"/>

级联风格(cascade style)是可组合的:

<one-to-one name="person" cascade="persist,delete,lock"/>

你可以使用cascade="all"来指定全部操作都顺着关联关系级联(cascaded)。 默认值是cascade="none",即任何操作都不会被级联(cascaded)。

注意有一个特殊的级联风格(cascade style) delete-orphan,只应用于one-to-many关联,表明delete()操作 应该被应用于所有从关联中删除的对象。

建议:

  • 通常在<many-to-one><many-to-many>关系中应用级联(cascade)没什么意义。 级联(cascade)通常在 <one-to-one><one-to-many>关系中比较有用。

  • 如果子对象的寿命限定在父亲对象的寿命之内,可通过指定cascade="all,delete-orphan"将其变为自动生命周期管理的对象(lifecycle object)

  • 其他情况,你可根本不需要级联(cascade)。但是如果你认为你会经常在某个事务中同时用到父对象与子对象,并且你希望少打点儿字,可以考虑使用cascade="persist,merge,save-update"

可以使用cascade="all"将一个关联关系(无论是对值对象的关联,或者对一个集合的关联)标记为父/子关系的关联。 这样对父对象进行save/update/delete操作就会导致子对象也进行save/update/delete操作。

此外,一个持久的父对象对子对象的浅引用(mere reference)会导致子对象被同步save/update。 不过,这个隐喻(metaphor)的说法并不完整。除非关联是<one-to-many>关联并且被标记为cascade="delete-orphan", 否则父对象失去对某个子对象的引用不会导致该子对象被自动删除。 父子关系的级联(cascading)操作准确语义如下:

  • 如果父对象被persist(),那么所有子对象也会被persist()

  • 如果父对象被merge(),那么所有子对象也会被merge()

  • 如果父对象被save()update()saveOrUpdate(),那么所有子对象则会被saveOrUpdate()

  • 如果某个持久的父对象引用了瞬时(transient)或者脱管(detached)的子对象,那么子对象将会被saveOrUpdate()

  • 如果父对象被删除,那么所有子对象也会被delete()

  • 除非被标记为cascade="delete-orphan"(删除“孤儿”模式,此时不被任何一个父对象引用的子对象会被删除), 否则子对象失掉父对象对其的引用时,什么事也不会发生。 如果有特殊需要,应用程序可通过显式调用delete()删除子对象。

最后,注意操作的级联可能是在调用期(call time)或者写入期(flush time)作用到对象图上的。所有的操作,如果允许,都在操作被执行的时候级联到可触及的关联实体上。然而,save-upatedelete-orphan是在Session flush的时候才作用到所有可触及的被关联对象上的。

10.12. 使用元数据

Hibernate中有一个非常丰富的元级别(meta-level)的模型,含有所有的实体和值类型数据的元数据。 有时这个模型对应用程序本身也会非常有用。 比如说,应用程序可能在实现一种“智能”的深度拷贝算法时, 通过使用Hibernate的元数据来了解哪些对象应该被拷贝(比如,可变的值类型数据), 那些不应该(不可变的值类型数据,也许还有某些被关联的实体)。

Hibernate提供了ClassMetadata接口,CollectionMetadata接口和Type层次体系来访问元数据。 可以通过SessionFactory获取元数据接口的实例。

Cat fritz = ......;
ClassMetadata catMeta = sessionfactory.getClassMetadata(Cat.class);

Object[] propertyValues = catMeta.getPropertyValues(fritz);
String[] propertyNames = catMeta.getPropertyNames();
Type[] propertyTypes = catMeta.getPropertyTypes();

// get a Map of all properties which are not collections or associations
Map namedValues = new HashMap();
for ( int i=0; i<propertyNames.length; i++ ) {
    if ( !propertyTypes[i].isEntityType() && !propertyTypes[i].isCollectionType() ) {
        namedValues.put( propertyNames[i], propertyValues[i] );
    }
}
分享到:
评论

相关推荐

    Hibernate框架参考文档

    10. 与对象共事; 11. 事务和并发; 12. 拦截器与事件(Interceptors and events); 13. 批量处理(Batch processing; 14. HQL: Hibernate查询语言; 15. 条件查询(Criteria Queries); 16. Native SQL查询; 17. 过滤数据;...

    Hibernate+中文文档

    10. 与对象共事 10.1. Hibernate对象状态(object states) 10.2. 使对象持久化 10.3. 装载对象 10.4. 查询 10.4.1. 执行查询 10.4.2. 过滤集合 10.4.3. 条件查询(Criteria queries) 10.4.4. 使用原生SQL的...

    java学习hibernate

    第5节-与对象共事.ppt可能讲解了Criteria查询、HQL查询、QBC(Query By Criteria)以及原生SQL查询的使用,以及如何进行分页、排序、聚合函数等操作。 6. **查询语言**: 第7节-查询.ppt应该会深入讨论HQL,它是...

    hibernate 体系结构与配置 参考文档(html)

    10. 与对象共事 10.1. Hibernate对象状态(object states) 10.2. 使对象持久化 10.3. 装载对象 10.4. 查询 10.4.1. 执行查询 10.4.1.1. 迭代式获取结果(Iterating results) 10.4.1.2. 返回元组(tuples)的查询 ...

    hibernate3.2中文文档(chm格式)

    10. 与对象共事 10.1. Hibernate对象状态(object states) 10.2. 使对象持久化 10.3. 装载对象 10.4. 查询 10.4.1. 执行查询 10.4.2. 过滤集合 10.4.3. 条件查询(Criteria queries) 10.4.4. 使用原生SQL的...

    HibernateAPI中文版.chm

    10. 与对象共事 10.1. Hibernate对象状态(object states) 10.2. 使对象持久化 10.3. 装载对象 10.4. 查询 10.4.1. 执行查询 10.4.2. 过滤集合 10.4.3. 条件查询(Criteria queries) 10.4.4. 使用原生SQL的...

    hibernate 总结

    ### 与对象共事 这一部分涵盖了Hibernate中对象的生命周期管理,包括如何使对象持久化、装载对象、查询、修改和删除持久对象,以及如何处理对象的脱管状态和自动状态检测。这些知识点对于理解和优化应用程序的性能...

    Hibernate中文详细学习文档

    10. 与对象共事 10.1. Hibernate对象状态(object states) 10.2. 使对象持久化 10.3. 装载对象 10.4. 查询 10.4.1. 执行查询 10.4.2. 过滤集合 10.4.3. 条件查询(Criteria queries) 10.4.4. 使用原生SQL的...

    Hibernate 中文 html 帮助文档

    10. 与对象共事 10.1. Hibernate对象状态(object states) 10.2. 使对象持久化 10.3. 装载对象 10.4. 查询 10.4.1. 执行查询 10.4.1.1. 迭代式获取结果(Iterating results) 10.4.1.2. 返回元组(tuples)的查询 10.4....

    最全Hibernate 参考文档

    10. 与对象共事 10.1. Hibernate对象状态(object states) 10.2. 使对象持久化 10.3. 装载对象 10.4. 查询 10.4.1. 执行查询 10.4.1.1. 迭代式获取结果(Iterating results) 10.4.1.2. 返回元组(tuples)的查询 10.4....

    Hibernate教程

    11. 与对象共事 11.1. Hibernate对象状态(object states) 11.2. 使对象持久化 11.3. 装载对象 11.4. 查询 11.4.1. 执行查询 11.4.1.1. 迭代式获取结果(Iterating results) 11.4.1.2. 返回元组(tuples)的查询 ...

    Hibernate_3.2.0_符合Java习惯的关系数据库持久化

    10. 与对象共事 10.1. Hibernate对象状态(object states) 10.2. 使对象持久化 10.3. 装载对象 10.4. 查询 10.4.1. 执行查询 10.4.2. 过滤集合 10.4.3. 条件查询(Criteria queries) 10.4.4. 使用原生SQL的...

    hibernate_reference中文文档.pdf

    - **1.1.7 加载并存储对象**:演示如何使用 Hibernate API 来加载数据库中的数据到 Java 对象,并将 Java 对象持久化到数据库。 ##### 1.2 第二部分 — 关联映射 此章节深入探讨了 Hibernate 中对象之间的关联映射...

    Hibernate3的帮助文档

    11. 与对象共事 11.1. Hibernate对象状态(object states) 11.2. 使对象持久化 11.3. 装载对象 11.4. 查询 11.4.1. 执行查询 11.4.1.1. 迭代式获取结果(Iterating results) 11.4.1.2. 返回元组(tuples)的查询 ...

    hibernate3.04中文文档.chm

    11. 与对象共事 11.1. Hibernate对象状态(object states) 11.2. 使对象持久化 11.3. 装载对象 11.4. 查询 11.4.1. 执行查询 11.4.1.1. 迭代式获取结果(Iterating results) 11.4.1.2. 返回元组(tuples)的查询 ...

    Hibernate3+中文参考文档

    10. 与对象共事 10.1. Hibernate对象状态(object states) 10.2. 使对象持久化 10.3. 装载对象 10.4. 查询 10.4.1. 执行查询 10.4.1.1. 迭代式获取结果(Iterating results) 10.4.1.2. 返回元组(tuples)的查询 10.4....

    hibernate 框架详解

    11. 与对象共事 11.1. Hibernate对象状态(object states) 11.2. 使对象持久化 11.3. 装载对象 11.4. 查询 11.4.1. 执行查询 11.4.1.1. 迭代式获取结果(Iterating results) 11.4.1.2. 返回元组(tuples)的...

    Hibernate参考文档

    10. 与对象共事 10.1. Hibernate对象状态(object states) 10.2. 使对象持久化 10.3. 装载对象 10.4. 查询 10.4.1. 执行查询 10.4.1.1. 迭代式获取结果(Iterating results) 10.4.1.2. 返回元组(tuples)的查询 10.4....

    大数据技术与应用专业人才培养综合方案.docx

    * Struts2 框架技术、Spring 框架技术、Hibernate 框架技术、SSH2 整合技术 * Android 开发、Java Web 应用开发、面向服务的架构设计 * 大数据技术与应用专业必修课程 大数据技术与应用专业人才培养综合方案旨在...

Global site tag (gtag.js) - Google Analytics