Hibernate.orgCommunity Documentation
Hibernate is a full object/relational mapping solution that not only shields the developer from the details of the underlying database management system, but also offers state management of objects. This is, contrary to the management of SQL statements
in common JDBC/SQL persistence layers, a natural object-oriented view of persistence in Java applications.
달리 말해, Hibernate 어플리케이션 개발자들은 그들의 객체들의 상태에 대해 항상 생각해야 하고, SQL 문장들의 실행에 대해서는 필수적이지 않다. 이 부분은 Hibernate에 의해 처리되고 시스템의 퍼포먼스를 튜닝할 때 어플리케이션 개발자와 유일하게 관련된다.
Hibernate 다음 객체 상태들을 정의하고 지원한다:
Transient - an object is transient if it has just been instantiated using the new
operator, and it is not associated with a Hibernate Session
. It has no persistent representation in the database and no identifier value has been assigned. Transient instances will be destroyed by the garbage collector if the application does not hold a reference anymore. Use the Hibernate Session
to make an object persistent (and let Hibernate take care of the SQL statements that need to be executed for this transition).
Persistent - a persistent instance has a representation in the database and an identifier value. It might just have been saved or loaded, however, it is by definition in the scope of a Session
. Hibernate will detect any changes made to an object in persistent state and synchronize the state with the database when the unit of work completes. Developers do not execute manual UPDATE
statements, or DELETE
statements when an object should be made transient.
Detached - a detached instance is an object that has been persistent, but its Session
has been closed. The reference to the object is still valid, of course, and the detached instance might even be modified in this state. A detached instance can be reattached to a new Session
at a later point in time, making it (and all the modifications) persistent again. This feature enables a programming model for long running units of work that require user think-time. We call them application transactions, i.e., a unit of work from the point of view of the user.
We will now discuss the states and state transitions (and the Hibernate methods that trigger a transition) in more detail.
하나의 영속 클래스의 새로이 초기화 된 인스턴스들은 Hibernate에 의해 transient로 간주된다. 우리는 그것을 세션과 연관지어서 transient 인스턴스를 영속화 시킬 수 있다:
DomesticCat fritz = new DomesticCat(); fritz.setColor(Color.GINGER); fritz.setSex('M'); fritz.setName("Fritz"); Long generatedId = (Long) sess.save(fritz);
If Cat
has a generated identifier, the identifier is generated and assigned to the cat
when save()
is called. If Cat
has an assigned
identifier, or a composite key, the identifier should be assigned to the cat
instance before calling save()
. You can also use persist()
instead of save()
, with the semantics defined in the EJB3 early draft.
persist()
makes a transient instance persistent. However, it does not guarantee that the identifier value will be assigned to the persistent instance immediately, the assignment might happen at flush time. persist()
also guarantees that it will not execute an INSERT
statement if it is called outside of transaction boundaries. This is useful in long-running conversations with an extended Session/persistence context.
save()
does guarantee to return an identifier. If an INSERT has to be executed to get the identifier ( e.g. "identity" generator, not "sequence"), this INSERT happens immediately, no matter if you are inside or outside of a transaction. This is problematic in a long-running conversation with an extended Session/persistence context.
Alternatively, you can assign the identifier using an overloaded version of 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) );
If the object you make persistent has associated objects (e.g. the kittens
collection in the previous example), these objects can be made persistent in any order you like unless you have a NOT NULL
constraint upon a foreign key column. There is never a risk of violating foreign key constraints. However, you might violate a NOT NULL
constraint if you save()
the objects in the wrong order.
Usually you do not bother with this detail, as you will normally use Hibernate's transitive persistence feature to save the associated objects automatically. Then, even NOT NULL
constraint violations do not occur - Hibernate will take care of everything. Transitive persistence is discussed later in this chapter.
The load()
methods of Session
provide a way of retrieving a persistent instance if you know its identifier. load()
takes a class object and loads the state into a newly instantiated instance of that class in a persistent 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) );
다른 방법으로 당신은 주어진 인스턴스 속으로 상태를 로드시킬 수 있다:
Cat cat = new DomesticCat(); // load pk's state into cat sess.load( cat, new Long(pkId) ); Set kittens = cat.getKittens();
Be aware that load()
will throw an unrecoverable exception if there is no matching database row. If the class is mapped with a proxy, load()
just returns an uninitialized proxy and does not actually hit the database until you invoke a method of the proxy. This is useful if you wish to create an association to an object without actually loading it from the database. It also allows multiple instances to be loaded as a batch if batch-size
is defined for the class mapping.
If you are not certain that a matching row exists, you should use the get()
method which hits the database immediately and returns null if there is no matching row.
Cat cat = (Cat) sess.get(Cat.class, id); if (cat==null) { cat = new Cat(); sess.save(cat, id); } return cat;
You can even load an object using an SQL SELECT ... FOR UPDATE
, using a LockMode
. See the API documentation for more information.
Cat cat = (Cat) sess.get(Cat.class, id, LockMode.UPGRADE);
Any associated instances or contained collections will not be selected FOR UPDATE
, unless you decide to specify lock
or all
as a cascade style for the association.
refresh()
메소드를 사용하여, 아무때나 하나의 객체와 모든 그것의 콜렉션들을 다시 로드시키는 것이 가능하다. 데이터베이스 트리거들이 그 객체의 프로퍼티들 중 어떤 것을 초기화 시키는데 사용될 때 이것이 유용하다.
sess.save(cat); sess.flush(); //force the SQL INSERT sess.refresh(cat); //re-read the state (after the trigger executes)
How much does Hibernate load from the database and how many SQL SELECT
s will it use? This depends on the fetching strategy. This is explained in 19.1절. “페칭 방도들”.
If you do not know the identifiers of the objects you are looking for, you need a query. Hibernate supports an easy-to-use but powerful object oriented query language (HQL). For programmatic query creation, Hibernate supports a sophisticated Criteria and Example query feature (QBC and QBE). You can also express your query in the native SQL of your database, with optional support from Hibernate for result set conversion into objects.
HQL 질의와 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());
A query is usually executed by invoking list()
. The result of the query will be loaded completely into a collection in memory. Entity instances retrieved by a query are in a persistent state. The uniqueResult()
method offers a shortcut if you know your query will only return a single object. Queries that make use of eager fetching of collections usually return duplicates of the root objects, but with their collections initialized. You can filter these duplicates through a Set
.
Occasionally, you might be able to achieve better performance by executing the query using the iterate()
method. This will usually be the case if you expect that the actual entity instances returned by the query will already be in the session or second-level cache. If they are not already cached, iterate()
will be slower than list()
and might require many database hits for a simple query, usually 1 for the initial select which only returns identifiers, and n additional selects to initialize the actual instances.
// 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; } }
Hibernate queries sometimes return tuples of objects. Each tuple is returned as an array:
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]; .... }
Queries can specify a property of a class in the select
clause. They can even call SQL aggregate functions. Properties or aggregates are considered "scalar" results and not entities in 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]; ..... }
Methods on Query
are provided for binding values to named parameters or JDBC-style ?
parameters. Contrary to JDBC, Hibernate numbers parameters from zero. Named parameters are identifiers of the form :name
in the query string. The advantages of named parameters are as follows:
명명된 파라미터들은 그것들이 질의 문자열 내에 발생하는 순서에 관계없다
they can occur multiple times in the same query
그것은 자기-설명적이다
//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();
If you need to specify bounds upon your result set, that is, the maximum number of rows you want to retrieve and/or the first row you want to retrieve, you can use methods of the Query
interface:
Query q = sess.createQuery("from DomesticCat cat"); q.setFirstResult(20); q.setMaxResults(10); List cats = q.list();
Hibernate는 이 limit 질의를 당신의 DBMS의 native SQL로 번역하는 방법을 알고 있다.
If your JDBC driver supports scrollable ResultSet
s, the Query
interface can be used to obtain a ScrollableResults
object that allows flexible navigation of the query results.
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()
Note that an open database connection and cursor is required for this functionality. Use setMaxResult()
/setFirstResult()
if you need offline pagination functionality.
You can also define named queries in the mapping document. Remember to use a CDATA
section if your query contains characters that could be interpreted as markup.
<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();
The actual program code is independent of the query language that is used. You can also define native SQL queries in metadata, or migrate existing queries to Hibernate by placing them in mapping files.
Also note that a query declaration inside a <hibernate-mapping>
element requires a global unique name for the query, while a query declaration inside a <class>
element is made unique automatically by prepending the fully qualified name of the class. For example eg.Cat.ByNameAndMaximumWeight
.
A collection filter is a special type of query that can be applied to a persistent collection or array. The query string can refer to this
, meaning the current collection element.
Collection blackKittens = session.createFilter( pk.getKittens(), "where this.color = ?") .setParameter( Color.BLACK, Hibernate.custom(ColorUserType.class) ) .list() );
The returned collection is considered a bag that is a copy of the given collection. The original collection is not modified. This is contrary to the implication of the name "filter", but consistent with expected behavior.
Observe that filters do not require a from
clause, although they can have one if required. Filters are not limited to returning the collection elements themselves.
Collection blackKittenMates = session.createFilter( pk.getKittens(), "select this.mate where this.color = eg.Color.BLACK.intValue") .list();
Even an empty filter query is useful, e.g. to load a subset of elements in a large collection:
Collection tenKittens = session.createFilter( mother.getKittens(), "") .setFirstResult(0).setMaxResults(10) .list();
HQL is extremely powerful, but some developers prefer to build queries dynamically using an object-oriented API, rather than building query strings. Hibernate provides an intuitive Criteria
query API for these cases:
Criteria crit = session.createCriteria(Cat.class); crit.add( Restrictions.eq( "color", eg.Color.BLACK ) ); crit.setMaxResults(10); List cats = crit.list();
Criteria
와 연관된 Example
API 는 15장. Criteria 질의들에서 상세하게 논의된다.
You can express a query in SQL, using createSQLQuery()
and let Hibernate manage the mapping from result sets to objects. You can at any time call session.connection()
and use the JDBC Connection
directly. If you choose to use the Hibernate API, you must enclose SQL aliases in braces:
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()
SQL queries can contain named and positional parameters, just like Hibernate queries. More information about native SQL queries in Hibernate can be found in 16장. Native SQL.
Transactional persistent instances (i.e. objects loaded, saved, created or queried by the Session
) can be manipulated by the application, and any changes to persistent state will be persisted when the Session
is flushed. This is discussed later in this chapter. There is no need to call a particular method (like update()
, which has a different purpose) to make your modifications persistent. The most straightforward way to update the state of an object is to load()
it and then manipulate it directly while the Session
is open:
DomesticCat cat = (DomesticCat) sess.load( Cat.class, new Long(69) ); cat.setName("PK"); sess.flush(); // changes to cat are automatically detected and persisted
Sometimes this programming model is inefficient, as it requires in the same session both an SQL SELECT
to load an object and an SQL UPDATE
to persist its updated state. Hibernate offers an alternate approach by using detached instances.
Hibernate does not offer its own API for direct execution of UPDATE
or DELETE
statements. Hibernate is a state management service, you do not have to think in statements to use it. JDBC is a perfect API for executing SQL statements, you can get a JDBC Connection
at any time by calling session.connection()
. Furthermore, the notion of mass operations conflicts with object/relational mapping for online transaction processing-oriented applications. Future versions of Hibernate can, however, provide special mass operation functions. See 13장. Batch ì²ë¦¬ for some possible batch operation tricks.
많은 어플리케이션들은 하나의 트랜잭션 내에서 하나의 객체를 검색하고, 처리를 위한 UI 계층으로 그것을 전송하고, 그런 다음 새로운 트랜잭션 내에서 변경들을 저장할 필요가 있다. 고도의-동시성 환경에서 이런 종류의 접근법을 사용하는 어플리케이션들은 대개 작업의 "긴" 단위를 확실히 격리시키기 위해 버전화 된 데이터를 사용한다.
Hibernate는 Session.update()
메소드 또는 Session.merge()
메소드를 사용하여 detached 인스턴스들의 재첨부를 제공함으로써 이 모형을 지원한다:
// 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
이 secondSession
에 의해 이미 로드되었을 경우에 어플리케이션이 그것을 다시 재첨부하려고 시도할 때, 예외상황이 던져졌을 것이다.
Use update()
if you are certain that the session does not contain an already persistent instance with the same identifier. Use merge()
if you want to merge your modifications at any time without consideration of the state of the session. In other words, update()
is usually the first method you would call in a fresh session, ensuring that the reattachment of your detached instances is the first operation that is executed.
The application should individually update()
detached instances that are reachable from the given detached instance only if it wants their state to be updated. This can be automated using transitive persistence. See 10.11절. “Transitive persistence(전이 영속)” for more information.
The lock()
method also allows an application to reassociate an object with a new session. However, the detached instance has to be unmodified.
//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);
Note that lock()
can be used with various LockMode
s. See the API documentation and the chapter on transaction handling for more information. Reattachment is not the only usecase for lock()
.
긴 작업 단위에 대한 다른 모형들은 11.3절. “Optimistic 동시성 제어”에서 논의된다.
Hibernate 사용자들은 새로운 식별자를 생성시켜서 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()
의 사용 예제와 의미는 초심자들에게는 혼동스러워 보인다. 먼저, 하나의 세션에서 온 인스턴스를 또 다른 새로운 세션 내에서 사용하려고 시도하지 않는 한, 당신은 update()
, saveOrUpdate()
, 또는 merge()
를 사용할 필요는 없을 것이다. 몇몇 전체 어플리케이션들은 이들 메소드들 중 어느 것도 결코 사용하지 않을 것이다.
대개 update()
또는 saveOrUpdate()
는 다음 시나리오에서 사용된다:
어플리케이션이 첫 번째 세션 내에 객체를 로드시킨다
객체가 UI 티어로 전달된다
몇몇 변경들이 그 객체에 행해진다
객체가 비지니스 로직 티어로 전달된다
어플리케이션은 두 번째 세션에서 update()
를 호출함으로써 이들 변경들을 영속화 시킨다
saveOrUpdate()
는 다음을 행한다:
만일 객체가 이 세션 내에서 이미 영속화 되어 있을 경우, 아무것도 행하지 않는다
만일 그 세션과 연관된 또 다른 객체가 동일한 식별자를 가질 경우, 예외상황을 던진다
만일 그 객체가 식별자 프로퍼티를 갖지 않을 경우, 그것을 save()
시킨다
만일 객체의 식별자가 새로이 초기화 된 객체에 할당된 값을 가질 경우, 그것을 save()
시킨다
if the object is versioned by a <version>
or <timestamp>
, and the version property value is the same value assigned to a newly instantiated object, save()
it
그 밖의 경우 그 객체를 update()
시킨다
그리고 merge()
는 매우 다르다:
만일 세션과 현재 연관된 동일한 식별자를 가진 영속 인스턴스가 존재할 경우, 주어진 객체의 상태를 영속 인스턴스 상으로 복사한다
만일 세션과 현재 연관된 영속 인스턴스가 존재하지 않을 경우, 데이터베이스로부터 그것을 로드시키려고 시도하거나 새로운 영속 인스턴스를 생성시키려고 시도한다
영속 인스턴스가 반환된다
주어진 인스턴스는 세션과 연관되지 않고, 그것은 detached 상태에 머무른다
Session.delete()
will remove an object's state from the database. Your application, however, can still hold a reference to a deleted object. It is best to think of delete()
as making a persistent instance, transient.
sess.delete(cat);
You can delete objects in any order, without risk of foreign key constraint violations. It is still possible to violate a NOT NULL
constraint on a foreign key column by deleting objects in the wrong order, e.g. if you delete the parent, but forget to delete the children.
It is sometimes useful to be able to take a graph of persistent instances and make them persistent in a different datastore, without regenerating identifier values.
//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();
The ReplicationMode
determines how replicate()
will deal with conflicts with existing rows in the database:
ReplicationMode.IGNORE
: ignores the object when there is an existing database row with the same identifier
ReplicationMode.OVERWRITE
: overwrites any existing database row with the same identifier
ReplicationMode.EXCEPTION
: throws an exception if there is an existing database row with the same identifier
ReplicationMode.LATEST_VERSION
: overwrites the row if its version number is earlier than the version number of the object, or ignore the object otherwise
이 특징의 쓰임새들은 다른 데이터베이스 인스턴스들 속으로 입력된 데이터 일치시키기, 제품 업그레이드 동안에 시스템 구성 정보 업데이트 하기, non-ACID 트랜잭션들 동안에 행해진 변경들을 롤백시키기 등을 포함한다.
Sometimes the Session
will execute the SQL statements needed to synchronize the JDBC connection's state with the state of objects held in memory. This process, called flush, occurs by default at the following points:
몇몇 질의들이 실행되기 전에
org.hibernate.Transaction.commit()
시점에서
Session.flush()
시점에서
The SQL statements are issued in the following order:
all entity insertions in the same order the corresponding objects were saved using Session.save()
모든 엔티티 업데이트들
모든 콜렉션 삭제들
모든 콜렉션 요소 삭제들, 업데이트들 그리고 삽입들
모든 콜렉션 삽입들
all entity deletions in the same order the corresponding objects were deleted using Session.delete()
An exception is that objects using native
ID generation are inserted when they are saved.
Except when you explicitly flush()
, there are absolutely no guarantees about when the Session
executes the JDBC calls, only the order in which they are executed. However, Hibernate does guarantee that the Query.list(..)
will never return stale or incorrect data.
It is possible to change the default behavior so that flush occurs less frequently. The FlushMode
class defines three different modes: only flush at commit time when the Hibernate Transaction
API is used, flush automatically using the explained routine, or never flush unless flush()
is called explicitly. The last mode is useful for long running units of work, where a Session
is kept open and disconnected for a long time (see 11.3.2절. “확장된 세션과 자동적인 버전화”).
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 오퍼레이션이 컨스트레인트를 위반할 경우). 예외상황들을 처리하는 것은 Hibernatem의 트랜잭션 특징에 관한 어떤 이해를 수반하며, 우리는 11장. Transactions and Concurrency에서 그것을 논의한다.
특히 당신이 연관된 객체들의 그래프를 다룰 경우에, 특히 개별 객체들을 저장하고, 삭제하거나, 재첨부시키는 것이 꽤 번거롭다. 공통된 경우는 하나의 부모/자식 관계이다. 다음 예제를 검토하자:
If the children in a parent/child relationship would be value typed (e.g. a collection of addresses or strings), their life cycle would depend on the parent and no further action would be required for convenient "cascading" of state changes. When the parent is saved, the value-typed child objects are saved and when the parent is deleted, the children will be deleted, etc. This works for operations such as the removal of a child from the collection. Since value-typed objects cannot have shared references, Hibernate will detect this and delete the child from the database.
Now consider the same scenario with parent and child objects being entities, not value-types (e.g. categories and items, or parent and child cats). Entities have their own life cycle and support shared references. Removing an entity from the collection does not mean it can be deleted), and there is by default no cascading of state from one entity to any other associated entities. Hibernate does not implement persistence by reachability by default.
- persist(), merge(), saveOrUpdate(), delete(), lock(), refresh(), evict(), replicate()
를 포함하는- Hibernate 세션에 대한 각각의 기본 오퍼레이션에 대해서 하나의 대응하는 케스케이딩 스타일이 존재한다. 케스케이드 스타일들 각각은 create, merge, save-update, delete, lock, refresh, evict, replicate
로 명명된다. 만일 당신이 하나의 오퍼레이션이 하나의 연관에 따라 케스케이딩되는 것을 원할 경우, 당신은 매핑 문서 내에 그것을 지시해야 한다. 예를 들면:
<one-to-one name="person" cascade="persist"/>
케스케이딩 스타일들이 결합될 수도 있다:
<one-to-one name="person" cascade="persist,delete,lock"/>
You can even use cascade="all"
to specify that all operations should be cascaded along the association. The default cascade="none"
specifies that no operations are to be cascaded.
특정한 케스케이드 스타일인, delete-orphan
은 오직 one-to-many 연관들에만 적용되고, delete()
오퍼레이션이 그 연관으로부터 제거되는 임의의 자식 객체에 적용되어야 함을 나타낸다.
권장사항들 :
It does not usually make sense to enable cascade on a <many-to-one>
or <many-to-many>
association. Cascade is often useful for <one-to-one>
and <one-to-many>
associations.
만일 자식 객체의 수명이 그 부모 객체의 수명에 묶여져 있을 경우, cascade="all,delete-orphan"
을 지정함으로써 그것을 생명 주기 객체로 만들어라.
그 밖의 경우, 당신은 케스케이드를 전혀 필요로 하지 않을 수 있다. 그러나 만일 당신이 동일한 트랜잭션 내에서 부모와 자식에 대해 자주 함께 작업하게 될 것이라 생각되고, 당신 스스로 타이핑 하는 것을 절약하고자 원할 경우, cascade="persist,merge,save-update"
를 사용하는 것을 고려하라.
cascade="all"
을 가진 (단일 값 연관이든 하나의 콜렉션이든) 하나의 연관을 매핑시키는 것은 그 연관을 부모의 저장/업데이트/삭제가 자식 또는 자식들의 저장/업데이트/삭제로 귀결되는 부모/자식 스타일의 관계로 마크한다.
Furthermore, a mere reference to a child from a persistent parent will result in save/update of the child. This metaphor is incomplete, however. A child which becomes unreferenced by its parent is not automatically deleted, except in the case of a <one-to-many>
association mapped with cascade="delete-orphan"
. The precise semantics of cascading operations for a parent/child relationship are as follows:
만일 부모가 persist()
에 전달될 경우, 모든 자식들이 persist()
에 전달된다
만일 부모가 merge()
에 전달될 경우, 모든 자식들이 merge()
에 전달된다
만일 부모가 save()
, update()
또는 saveOrUpdate()
에 전달될 경우, 모든 자식들이 saveOrUpdate()
에 전달된다
만일 transient 또는 detached 자식이 영속 부모에 의해 참조될 경우, 그것은 saveOrUpdate()
에 전달된다
만일 부모가 삭제될 경우, 모든 자식들이 delete()
에 전달된다
만일 자식이 영속 부모에 의해 참조 해제 될 경우, cascade="delete-orphan"
이 아닌 한, 특별한 어떤 것도 발생하지 않는다 - 어플리케이션은 필요한 경우에 자식을 명시적으로 삭제해야 한다 -, cascade="delete-orphan"
인 경우에 "orphaned(고아)"인 경우 자식이 삭제된다.
Finally, note that cascading of operations can be applied to an object graph at call time or at flush time. All operations, if enabled, are cascaded to associated entities reachable when the operation is executed. However, save-update
and delete-orphan
are transitive for all associated entities reachable during flush of the Session
.
Hibernate requires a rich meta-level model of all entity and value types. This model can be useful to the application itself. For example, the application might use Hibernate's metadata to implement a "smart" deep-copy algorithm that understands which objects should be copied (eg. mutable value types) and which objects that should not (e.g. immutable value types and, possibly, associated entities).
Hibernate exposes metadata via the ClassMetadata
and CollectionMetadata
interfaces and the Type
hierarchy. Instances of the metadata interfaces can be obtained from the 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] ); } }
저작권 © 2004 Red Hat Middleware, LLC.