Neste artigo iremos demonstrar os vários tipos de cascade que o Hibernate nos oferece afim de facilitar as operações de persistência no banco. O cascade não é um recurso específico do Hibernate, este já vem de longa data e é um conceito sólido em se tratando de banco de dados.

Como o próprio nome já diz, a função do cascade é cascatear operações de persistência. Com isso você já deve perceber que este é utilizada apenas quando existe algum tipo de relacionamento entre 2 ou mais classes.

Cascade save-update

Vamos iniciar pelo cascade save-update. E como o nome sugere, quando é feito um save ou um update na classe “Pai” as classes “Filhas” também são salvas ou atualizadas. Veja na listagem 1 como ficaria um cascade sem save-update.

Listagem 1: Cascade sem save-update


Stock stock = new Stock();
StockDailyRecord stockDailyRecords = new StockDailyRecord();
 
stockDailyRecords.setStock(stock);        
stock.getStockDailyRecords().add(stockDailyRecords);
 
session.save(stock);
session.save(stockDailyRecords);

O que você percebe acima ? Temos que salvar o Stock e depois salvar o StockDailyRecords, ou seja, temos que explicitamente salvar a classe “Filha”. Veja abaixo a saída do código acima no Hibernate.

Listagem 2: Saida da Listagem 1


 Hibernate:
    insert into stock (STOCK_CODE, STOCK_NAME) 
    values (?, ?) 
 Hibernate: 
    insert into stock_daily_record
    (STOCK_ID, PRICE_OPEN, PRICE_CLOSE, PRICE_CHANGE, VOLUME, DATE) 
    values (?, ?, ?, ?, ?, ?)

Agora vamos utilizar o save-update e ver o resultado do mesmo.

Listagem 3: Mapeando com cascade save-update


<!-- Stock.hbm.xml -->
<set name="stockDailyRecords" cascade="save-update" table="stock_daily_record"...>
      <key>
            <column name="STOCK_ID" not-null="true" />
      </key>
      <one-to-many class="br.com.myproject.StockDailyRecord" />
</set>

Stock stock = new Stock();
StockDailyRecord stockDailyRecords = new StockDailyRecord();
 
stockDailyRecords.setStock(stock);        
stock.getStockDailyRecords().add(stockDailyRecords);
 
session.save(stock);

Perceba acima que estamos salvando apenas o Stock e tudo será cascateado na classe Filha, veja a saída do código abaixo.

Listagem 4: Saída da Listagem 3.

Hibernate: 
    insert into stock (STOCK_CODE, STOCK_NAME) 
    values (?, ?) 
 Hibernate: 
    insert into stock_daily_record
    (STOCK_ID, PRICE_OPEN, PRICE_CLOSE, PRICE_CHANGE, VOLUME, DATE) 
    values (?, ?, ?, ?, ?, ?)

O código da listagem 4 é apenas para demonstrar que a saída será a mesma independente de usar ou não o cascade. Porém para um desenvolvimento baseado nas boas práticas é aconselhável o uso do mesmo.

Cascade Delete

O cascade delete faz com que os filhos sejam deletados se o pai for deletado, é a mesma lógica de Nota fisca e ItemNotafiscal, ou seja, se deletarmos a Nota fiscal os itens devem ser deletados. Veja como ficaria se não utilizarmos o cascade delete.

Listagem 5: Sem cascade delete


Query q = session.createQuery("from Stock where stockCode = :stockCode ");
q.setParameter("stockCode", "123");
Stock stock = (Stock)q.list().get(0);
 
for (StockDailyRecord sdr : stock.getStockDailyRecords()){
         session.delete(sdr);
}
 session.delete(stock);

Tivemos que fazer um laço para deletar item por item do Stock. Poderíamos resolver isso apenas adicionando o cascade delete.

Listagem 6: Usando cascade delete


<!-- Stock.hbm.xml -->
<set name="stockDailyRecords" cascade="delete" table="stock_daily_record" ...>
      <key>
            <column name="STOCK_ID" not-null="true" />
      </key>
      <one-to-many class="br.com.myproject.StockDailyRecord" />
</set>

Query q = session.createQuery("from Stock where stockCode = :stockCode ");
q.setParameter("stockCode", "123");
Stock stock = (Stock)q.list().get(0);
session.delete(stock);

Perceba que não precisamos mais do laço, já que utilizamos o cascade delete.

Cascade delete-orphan

Existe ainda o delete-orphan, quando você salva ou atualiza a classe pai, os registros filhos que estavam marcados como removidos são de fato deletados. Vamos pegar o exemplo da classe Stock e StockDailyRecord. Se você estiver gerenciando um objeto Stock que possui vários itens StockDailyRecord, freqüentemente você pode querer deletar 1 ou mais StockDailyRecord e salvar pelo Stock, através do delete-orphan isso é possível. Vamos ver exemplos nas listagens abaixo.

Listagem 7: Sem cascade delete-orphan


StockDailyRecord sdr1 = (StockDailyRecord)session.get(StockDailyRecord.class, 
                                            new Integer(56));
StockDailyRecord sdr2 = (StockDailyRecord)session.get(StockDailyRecord.class, 
                                            new Integer(57));
 
session.delete(sdr1);
session.delete(sdr2);

Perceba acima que você terá que explicitamente dizer qual item você quer deletar, isso na hora do desenvolvimento pode “pesar” muito já que você terá que gravar os ID de cada item que deverá ser deletado posteriormente. Veja como nosso exemplo fica muito mais fácil com o cascade delete-orphan.

Listagem 8: Usando cascade delete-orphan


<!-- Stock.hbm.xml -->
<set name="stockDailyRecords" cascade="delete-orphan" table="stock_daily_record" >
      <key>
            <column name="STOCK_ID" not-null="true" />
      </key>
      <one-to-many class="br.com.myproject.StockDailyRecord" />
</set>

StockDailyRecord sdr1 = (StockDailyRecord)session.get(StockDailyRecord.class, 
                                       new Integer(56));
StockDailyRecord sdr2 = (StockDailyRecord)session.get(StockDailyRecord.class, 
                                       new Integer(57));
 
Stock stock = (Stock)session.get(Stock.class, new Integer(2));
stock.getStockDailyRecords().remove(sdr1);
stock.getStockDailyRecords().remove(sdr2);
 
session.saveOrUpdate(stock);

Nos tipos de Cascade vistos neste artigo utilizamos o mapeamento em XML, porém nada impede que você os use via anotações.

Listagem 9: Usando cascade por anotações


        @OneToMany(mappedBy = "stock")
        @Cascade({CascadeType.SAVE_UPDATE, CascadeType.DELETE})
	public Set<StockDailyRecord> getStockDailyRecords() {
		return this.stockDailyRecords;
	}

Conclusão

Com certeza este é um assunto muito importante e essencial para o desenvolvimento de projetos usando Hibernate.