[Hibernate] Mutable (class và collection)

Trong Hibernate, "mutable" mặc định là "true" trong class và collection của nó, nghĩa là class hoặc collection cho phép insert, update và delete. Mặt khác, nếu mutable thay đổi là false, có ý nghĩa khác nhau trong class và colection của nó. Lấy một vài ví dụ để hiểu rõ hơn về nó.

Hibernate one-to-many
Lấy ví dụ one-to-many để trình bày về mutable. Trong file mapping này, một Stock liên qua đến nhiều StockDailyRecord.

XML:
<!-- Stock.hbm.xml -->
...
<hibernate-mapping>
    <class name="com.mkyong.common.Stock" table="stock" >
        <set name="stockDailyRecords" mutable="false" cascade="all"
               inverse="true" lazy="true" table="stock_daily_record">
            <key>
                <column name="STOCK_ID" not-null="true" />
            </key>
            <one-to-many class="com.mkyong.common.StockDailyRecord" />
        </set>
    </class>
...
</hibernate-mapping>

Làm sao để khai báo mutable ?
‘mutable’ hỗ trợ cả trong file mapping XML và annotation.


1. XML mapping file
Trong file mapping, keyword ‘mutable‘ được sử dụng để implement mutable function.

XML:
<!-- Stock.hbm.xml -->
...
<hibernate-mapping>
    <class name="com.mkyong.common.Stock" table="stock" mutable="false" >
        <set name="stockDailyRecords" mutable="false" cascade="all"
               inverse="true" lazy="true" table="stock_daily_record" >
            <key>
                <column name="STOCK_ID" not-null="true" />
            </key>
            <one-to-many class="com.mkyong.common.StockDailyRecord" />
        </set>
    </class>
...
</hibernate-mapping>

2. Annotation
Với annotation, keyword này được thay đổi thành @Immutable (mutable=’false’).

Java:
...
@Entity
@Immutable
@Table(name = "stock", catalog = "mkyong")
public class Stock implements java.io.Serializable {
...
        @OneToMany(fetch = FetchType.LAZY, mappedBy = "stock")
    @Immutable
    public Set<StockDailyRecord> getStockDailyRecords() {
        return this.stockDailyRecords;
    }
...

Mutable trong class
Nếu mutable = "false" hay @Immutable được khai báo trong class, điều đó có nghĩa là các lệnh update cho class này sẽ bị bỏ qua, không có exception nào được ném ra, chỉ cho phép thao tác insert và delete.


1. Test insert
Java:
Stock stock = new Stock();
stock.setStockCode("7277");
stock.setStockName("DIALOG");
session.save(stock);
Nếu mutable = “true” (default) hoặc không có khai báo @Immutable trong classs
Kết quả
Code:
Hibernate:
    insert into mkyong.stock (STOCK_CODE, STOCK_NAME)
    values (?, ?)
Nếu mutable = “false” hoặc @Immutable được khai báo trong classs
Kết quả

Code:
Hibernate:
    insert into mkyong.stock (STOCK_CODE, STOCK_NAME)
    values (?, ?)
Mutable trong class không gây ảnh hưởng đến thao tác insert.


2. Test update
Java:
Stock stock = (Stock)session.createQuery(
      " from Stock where stockCode = '7277'").list().get(0);
stock.setStockName("DIALOG123");
session.saveOrUpdate(stock);
Nếu mutable = “true” (default) hoặc không có khai báo @Immutable trong classs
Kết quả
Code:
Hibernate:
    select ...from mkyong.stock stock0_
    where stock0_.STOCK_CODE='7277'

Hibernate:
    update mkyong.stock
    set STOCK_CODE=?,STOCK_NAME=?
    where STOCK_ID=?
Nếu mutable = “false” hoặc @Immutable được khai báo trong classs
Kết quả
Code:
Hibernate:
    select ...from mkyong.stock stock0_
    where stock0_.STOCK_CODE='7277'
Mutable trong class không cho phép update nó, hoạt động "update" sẽ bị từ chối và không nén ra ngoại lệ.


3. Test delete

Java:
Stock stock = (Stock)session.createQuery(
      " from Stock where stockCode = '7277'").list().get(0);
session.delete(stock);
Nếu mutable = “true” (default) hoặc không có khai báo @Immutable trong classs
Kết quả
Code:
Hibernate:
    delete from mkyong.stock
    where STOCK_ID=?
Nếu mutable = “false” hoặc @Immutable được khai báo trong classs
Kết quả

Code:
Hibernate:
    delete from mkyong.stock
    where STOCK_ID=?
Mutable trong class không gây ảnh hưởng đến thao tác delete.


Mutable trong collection
Nếu mutable = “false” hoặc @Immutable được khai báo trong collection, nó nghĩa là insert và delete-orphan không được cho phép trong collection, với ngoại lệ ném ra, chỉ update và ‘cascade delete all’ được cho phép.


1. Test insert
Giả sử insert được enable.

Java:
Stock stock = (Stock)session.createQuery(
      " from Stock where stockCode = '7277'").list().get(0);
StockDailyRecord sdr = new StockDailyRecord();
sdr.setDate(new Date());
sdr.setStock(stock);
stock.getStockDailyRecords().add(sdr);
session.save(stock);
Nếu mutable = “true” (default) hoặc không có khai báo @Immutable trong classs
Kết quả
Code:
Hibernate:
    insert into mkyong.stock_daily_record
    (STOCK_ID, PRICE_OPEN, PRICE_CLOSE, PRICE_CHANGE, VOLUME, DATE)
    values (?, ?, ?, ?, ?, ?)
Nếu mutable = “false” hoặc @Immutable được khai báo trong classs
Kết quả

Code:
Exception in thread "main" org.hibernate.HibernateException:
changed an immutable collection instance:
[com.mkyong.common.Stock.stockDailyRecords#111]
Mutable trong coolection gây ảnh hưởng đến thao tác insert và có exception được nén ra.


2. Test update
Giả sử update được enable.

Java:
Stock stock = (Stock)session.createQuery(
      " from Stock where stockCode = '7277'").list().get(0);
StockDailyRecord sdr = stock.getStockDailyRecords().iterator().next();
sdr.setPriceChange(new Float(1.30));
session.saveOrUpdate(stock);
Nếu mutable = “true” (default) hoặc không có khai báo @Immutable trong classs
Kết quả
Code:
Hibernate:
    update mkyong.stock_daily_record
    set PRICE_CHANGE=?, ...
    where DAILY_RECORD_ID=?
Nếu mutable = “false” hoặc @Immutable được khai báo trong classs
Kết quả

Code:
Hibernate:
    update mkyong.stock_daily_record
    set PRICE_CHANGE=?, ...
    where DAILY_RECORD_ID=?
Mutable trong coolection không gây ảnh hưởng đến thao tác update.


3. Test delete-orphan
Giả sử cascade delete-orphan được enable.

Java:
Stock stock = (Stock)session.createQuery(
      " from Stock where stockCode = '7277'").list().get(0);
StockDailyRecord sdr = stock.getStockDailyRecords().iterator().next();
stock.getStockDailyRecords().remove(sdr);
session.saveOrUpdate(stock);
Nếu mutable = “true” (default) hoặc không có khai báo @Immutable trong classs
Kết quả
Code:
Hibernate:
    delete from mkyong.stock_daily_record
    where DAILY_RECORD_ID=?
Nếu mutable = “false” hoặc @Immutable được khai báo trong classs
Kết quả

Code:
Exception in thread "main" org.hibernate.HibernateException:
changed an immutable collection instance:
[com.mkyong.common.Stock.stockDailyRecords#111]
Mutable trong coolection gây ảnh hưởng đến thao tác delete-orphan và có exception được nén ra.


4. Test delete
Giả sử delete được enable.

Java:
Stock stock = (Stock)session.createQuery(
      " from Stock where stockCode = '7277'").list().get(0);
session.saveOrUpdate(stock);
Nếu mutable = “true” (default) hoặc không có khai báo @Immutable trong classs
Kết quả
Code:
Hibernate:
    delete from mkyong.stock_daily_record
    where DAILY_RECORD_ID=?

Hibernate:
    delete from mkyong.stock
    where STOCK_ID=?
Nếu mutable = “false” hoặc @Immutable được khai báo trong classs
Kết quả

Code:
Hibernate:
    delete from mkyong.stock_daily_record
    where DAILY_RECORD_ID=?

Hibernate:
    delete from mkyong.stock
    where STOCK_ID=?
Mutable trong coolection không gây ảnh hưởng đến thao tác delete, nếu đối tượng cha bị delete, tất cả các con của nó sẽ bị delete theo với điều kiện là mutable.


Vì sao sử dụng mutable ?
Mutable có thể tránh nhiều hoạt động không chủ ý tại database, như insert, update và delete vài record. Ngoài ra, theo tài liệu của Hibernate, mutable có vài tối ưu hóa performance nhỏ, nó luôn luôn đề nghị phân tích mapping relationship và thực hiện các biến đổi khi cần thiết.


Tóm tắt
1. mutable = “false” hoặc @Immutable được khai báo trong class
Nó nghĩa là update cho class sẽ bị từ chối, nhưng nó không ném ra exception ,chỉ cho phép hoạt động insert và delete.

Class có mutable=”false” – insert=allow, delete=allow , update=not allow

2. mutable = “false” trong @Immutable được khai báo cho collection
Nó nghĩa là insert và delete-orphan không được cho phép trong collection, với exception được ném ra, chỉ cho phép update. Tuy nhiên, nếu delete được enable, khi cha bị delete, tất cả con của nó sẽ bị delete, điều kiện là mutable.

Collection với mutable=”false” – insert=not allow, delete-orphan=not allow, delete=allow , update=allow


Bất biến với immutable ?
Một class có thể hoàn toàn bất biến đối với bất kỳ hành động nào? Có thể, thêm mutable=”false” cho các property tham chiếu đến đối tượng khác của nó (insert=not allow, delete-orphan=not allow) và mutable=”false” cho class muốn bất biến (update=not allow).Bây giờ, ta có một class hoàn toàn bất biến, tuy nhiên, nếu cascade delete được enable, khi đối tượng cha của object thuộc class bất biến bị delete, nó vẫn bị delete theo.


Cám ơn các bạn đã theo dõi. Hẹn gặp lại các bạn trong các bài viết sau :D

Bài viết tham khảo tại: https://mkyong.com/hibernate/hibernate-mutable-example-class-and-collection/
 

Bình luận