Hibernate 3.6.10.Final ²Î¿¼ÊÖ²á ÖÐÎÄ°æ

Hibernate

Hibernate.org Community Documentation

  • 瞬时(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)。

DomesticCat fritz = new DomesticCat();
fritz.setColor(Color.GINGER);

fritz.setSex('M');
fritz.setName("Fritz");
Long generatedId = (Long) sess.save(fritz);
  • persist() 使一个临时实例持久化。然而,它不保证立即把标识符值分配给持久性实例,这会发生在冲刷(flush)的时候。persist() 也保证它在事务边界外调用时不会执行 INSERT 语句。这对于长期运行的带有扩展会话/持久化上下文的会话是很有用的。

  • save() 保证返回一个标识符。如果需要运行 INSERT 来获取标识符(如 "identity" 而非 "sequence" 生成器),这个 INSERT 将立即执行,不管你是否在事务内部还是外部。这对于长期运行的带有扩展会话/持久化上下文的会话来说会出现问题。

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) );
        
        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) );
Cat cat = new DomesticCat();

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

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

sess.flush(); //force the SQL INSERT
sess.refresh(cat); //re-read the state (after the trigger executes)
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());
// 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;
    }
}
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 = (Cat) tuple[0];
    Cat mother = (Cat) tuple[1];
    ....
}
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];
    .....
}
  • 命名参数(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();
Query q = sess.createQuery("from DomesticCat cat");

q.setFirstResult(20);
q.setMaxResults(10);
List cats = q.list();
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()
@Entity

@NamedQuery(name="night.moreRecentThan", query="select n from Night n where n.date >= :date")
public class Night {
    ...
}
public class MyDao {
    doStuff() {
        Query q = s.getNamedQuery("night.moreRecentThan");
        q.setDate( "date", aMonthAgo );
        List results = q.list();
        ...
    }
    ...
}      

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

Query q = sess.getNamedQuery("ByNameAndMaximumWeight");

q.setString(0, name);
q.setInt(1, minWeight);
List cats = q.list();

Collection blackKittens = session.createFilter(

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

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

    mother.getKittens(), "")
    .setFirstResult(0).setMaxResults(10)
    .list();
Criteria crit = session.createCriteria(Cat.class);

crit.add( Restrictions.eq( "color", eg.Color.BLACK ) );
crit.setMaxResults(10);
List cats = crit.list();
List cats = session.createSQLQuery("SELECT {cat.*} FROM CAT {cat} WHERE ROWNUM<10")

    .addEntity("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")
    .addEntity("cat", Cat.class)
.list()
DomesticCat cat = (DomesticCat) sess.load( Cat.class, new Long(69) );

cat.setName("PK");
sess.flush();  // changes to cat are automatically detected and persisted
// 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
//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);
// 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)
  • 程序在第一个 session 中加载对象

  • 该对象被传递到表现层

  • 对象发生了一些改动

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

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

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

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

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

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

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

  • 否则 update() 这个对象

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

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

  • 最后返回该持久实例

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

        
        sess
        
        .
        
        delete
        
        (
        
        cat
        
        );
      
//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.IGNORE:当某个现有数据库记录具有相同标识符时忽略它

  • ReplicationMode.OVERWRITE:用相同的标识符覆盖现有数据库记录

  • ReplicationMode.EXCEPTION:当某个现有数据库记录具有相同标识符时抛出异常

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

  • 在某些查询执行之前

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

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

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

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

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

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

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

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

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();
<one-to-one name="person" cascade="persist"/>
<one-to-one name="person" cascade="persist,delete,lock"/>
  • CascadeType.PERSIST: cascades the persist (create) operation to associated entities persist() is called or if the entity is managed

  • CascadeType.MERGE: cascades the merge operation to associated entities if merge() is called or if the entity is managed

  • CascadeType.REMOVE: cascades the remove operation to associated entities if delete() is called

  • CascadeType.REFRESH: cascades the refresh operation to associated entities if refresh() is called

  • CascadeType.DETACH: cascades the detach operation to associated entities if detach() is called

  • CascadeType.ALL: all of the above

注意

CascadeType.ALL also covers Hibernate specific operations like save-update, lock etc...

@Entity 

public class Customer {
   private Set<Order> orders;
   @OneToMany(cascade=CascadeType.ALL, orphanRemoval=true) 
   public Set<Order> getOrders() { return orders; }
   public void setOrders(Set<Order> orders) { this.orders = orders; }
   [...]
}
@Entity 
public class Order { ... }
Customer customer = em.find(Customer.class, 1l);
Order order = em.find(Order.class, 1l);
customer.getOrders().remove(order); //order will be deleted by cascade

  • It does not usually make sense to enable cascade on a many-to-one or many-to-many association. In fact the @ManyToOne and @ManyToMany don't even offer a orphanRemoval attribute. Cascading is often useful for one-to-one and one-to-many associations.

  • If the child object's lifespan is bounded by the lifespan of the parent object, make it a life cycle object by specifying cascade="all,delete-orphan"(@OneToMany(cascade=CascadeType.ALL, orphanRemoval=true)).

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

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

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

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

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

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

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

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] );
    }
}