Arquivo de março, 2015

Hibernate – One To One Mapping – Oracle – Java

Publicado: 28 de março de 2015 em Java

Linguagens orientadas a objeto utilizam atualmente frameworks ORM para persistir objetos no banco de dados relacional. Um clássico relacionamento entre duas tabelas em um banco de dados é feito através de uma chave primaria que contem um identificador em uma tabela e dados adicionais e uma chave estrangeira em outra tabela que define a referencia ao seu registro mestre, este tipo de referencia é comumente conhecido como referencia pai e filho ou mestre e detalhes. Ao longo dos anos os dados alimentados em tabelas mestre/detalhes foram inseridos através de scripts SQL escritos manualmente, ou seja se faz necessário que o desenvolvedor escreva um script pra inserir os dados na tabela mestre primeiro e depois escreva outro script para inserir os dados na tabela de detalhes, a chave estrangeira previne que somente seja inseridos dados na tabela de detalhes no qual possui uma referencia na tabela mestre, tornando assim o banco de dados integro.

Os frameworks modernos nos permitem persistir objetos utilizando a sintaxe da programação orientada a objeto de sua preferencia ao invés de usar scripts SQL tradicionais, pois estes serão criados automaticamente pelo próprio framework. Uma das vantagens de se usar este tipo de framework é que você pode persistir dados diretamente de seus objetos não precisando escrever linhas de código SQL. No entanto é preciso que você domine os conceitos exigidos pelo framework para criar cada tipo de recurso que a linguagem SQL oferece para que seu código rode redondo e com ótimo desempenho.

No caso do Hibernate é necessário que você utilize corretamente as anotações pra que o framework saiba como funciona o relacionamento entre as duas tabelas.

One To One: http://docs.jboss.org/hibernate/orm/4.3/manual/en-US/html_single/#d5e3678

One To One Mapping

1 – Primeiramente crie duas tabelas utilizando o script abaixo no banco de dados Oracle e crie sua foreign key adequadamente, caso a chave estrangeira esteja incorreta o Hibernate não conseguira utilizar o relacionamento adequadamente:

Oracle - FK - Chave Estrangeira

Oracle – FK – Chave Estrangeira

2 – Crie um novo projeto Java SE e utilize os códigos abaixo para suas respectivas classes. Atente-se para as anotações pois elas são importantes e precisam estar corretamente decoradas em seus atributos ou métodos, nunca misture as anotações entre atributos e métodos:

Hibernate - Persistindo Dados - One To One

Hibernate – Persistindo Dados – One To One

3 – Após rodar sua aplicação abra o Oracle SQL Developer e veja o conteúdo das duas tabelas. Você pode reparar que com apenas uma Sequence criando o identificador você automaticamente gravou os dados mestre/detalhes fazendo referencia ao seu ID:

Oracle SQL Developer - Resultado

Oracle SQL Developer – Resultado

Exemplo:

Neste exemplo criamos duas tabelas e uma chave estrangeira que faz referencia ao seu identificador criando um relacionamento mestre/detalhes. Utilizamos o framework Hibernate e seu relacionamento One To One para persistir dois objetos que foram mapeados adequadamente para salvar registros mestres e seus detalhes mantendo a integridade relacional no banco de dados.

SQL

-- Cria Sequence Participante
CREATE SEQUENCE SEQ_CARROS
 START WITH     1
 INCREMENT BY   1
 NOCACHE
 NOCYCLE;

-- Cria tabela de participantes
CREATE TABLE CARROS
(
  ID_CARRO INTEGER NOT NULL
, DESCRICAO VARCHAR2(30) NOT NULL

, CONSTRAINT CARROS_PK PRIMARY KEY
  (
    ID_CARRO
  )
  ENABLE
);

-- Cria tabela de carro_detalhes
CREATE TABLE CARROS_DETALHES
(
  ID_CARRO INTEGER NOT NULL
, MARCA VARCHAR2(30) NOT NULL
, MODELO VARCHAR2(30) NOT NULL
, ANO INTEGER NOT NULL

, CONSTRAINT CARROS_DETALHES_PK PRIMARY KEY
  (
    ID_CARRO
  )
  ENABLE

);

-- Cria Contraint chave estrangeira
ALTER TABLE CARROS_DETALHES ADD CONSTRAINT CARRO_FK
FOREIGN KEY  (ID_CARRO) REFERENCES CARROS (ID_CARRO);

Java

Carros

package org.desenvolvimento.aberto;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;

@Entity
@Table(name = "CARROS")
public class Carros {

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_CARROS")
	@SequenceGenerator(name = "SEQ_CARROS", sequenceName = "SEQ_CARROS")
	@Column(name = "ID_CARRO")
	private int id_carro;

	@Column(name = "DESCRICAO")
	private String descricao;

	// Cria instancia dos detalhes
	@OneToOne(mappedBy= "carros", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
	@JoinColumn(name = "ID_CARRO")
	private CarrosDetalhes carroDetalhes;

	// Getters & Setters Carros
	public int getId_carro() {
		return id_carro;
	}

	public void setId_carro(int id_carro) {
		this.id_carro = id_carro;
	}

	public String getDescricao() {
		return descricao;
	}

	public void setDescricao(String descricao) {
		this.descricao = descricao;
	}

	// Getter & Setter detalhes
	public CarrosDetalhes getCarroDetalhes() {
		return carroDetalhes;
	}

	public void setCarroDetalhes(CarrosDetalhes carroDetalhes) {
		this.carroDetalhes = carroDetalhes;
	}

}

CarrosDetalhes

package org.desenvolvimento.aberto;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;

import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;

@Entity
@Table(name = "CARROS_DETALHES")
public class CarrosDetalhes {

	@GenericGenerator(name = "generator", strategy = "foreign", parameters = @Parameter(name = "property", value = "carros"))
	@Id
	@GeneratedValue(generator = "generator")
	@Column(name = "ID_CARRO")
	private int id_carro;

	@Column(name = "MARCA")
	private String marca;

	@Column(name = "MODELO")
	private String modelo;

	@Column(name = "ANO")
	private int ano;

	@OneToOne(fetch = FetchType.LAZY)
	@PrimaryKeyJoinColumn
	private Carros carros;

	public int getId_carro() {
		return id_carro;
	}

	public void setId_carro(int id_carro) {
		this.id_carro = id_carro;
	}

	public String getMarca() {
		return marca;
	}

	public void setMarca(String marca) {
		this.marca = marca;
	}

	public String getModelo() {
		return modelo;
	}

	public void setModelo(String modelo) {
		this.modelo = modelo;
	}

	public int getAno() {
		return ano;
	}

	public void setAno(int ano) {
		this.ano = ano;
	}

	public Carros getCarros() {
		return carros;
	}

	public void setCarros(Carros carros) {
		this.carros = carros;
	}

}

Teste

package org.desenvolvimento.aberto;

import org.hibernate.Session;

public class Testar {

	public static void main(String[] args) {

		// Cria sessão
		Session session = DBConexaoFactory.getSessionFactory().openSession();

		// Cria transação
		session.beginTransaction();

		// Cria objeto de detalhes
		CarrosDetalhes detalhes = new CarrosDetalhes();

		detalhes.setMarca("Honda");
		detalhes.setModelo("Civic");
		detalhes.setAno(2015);

		// Cria objeto carro
		Carros carros = new Carros();
		carros.setDescricao("Honda");

		// adiciona detalhes ao carro
		carros.setCarroDetalhes(detalhes);
		detalhes.setCarros(carros);

		// salva carro
		session.save(carros);

		// Confirma e encerra transação
		session.getTransaction().commit();

	}
}

DbConexaoFactory

package org.desenvolvimento.aberto;

import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class DBConexaoFactory {

	private static final SessionFactory sessionFactory = buildSessionFactory();

    // Constroi sessão
    @SuppressWarnings("deprecation")
    private static SessionFactory buildSessionFactory() {
        try {
            // buildSessionFactory não será utilizado em versões superiores
            // Veremos outros métodos para criar um Factory
            // Não é necessário incluir o "hibernate.cfg.xml" no configure()
            // Incluímos somente a nível de fácil entendimento da chamada da
            // configuração.
            // Você pode retirar a chamada.
            return new Configuration().configure("hibernate.cfg.xml")
                    .buildSessionFactory();
        } catch (Throwable ex) {
            // Em caso de erro
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    // Retorna Factory da sessão
    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    // Encerra Sessão
    public static void shutdown() {
        getSessionFactory().close();
    }
}

Hibernate.cfg.xml

<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>

<session-factory>
<property name="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
<property name="hibernate.connection.url">jdbc:oracle:thin:@localhost:1521:xe</property>
<property name="hibernate.connection.username">user</property>
<property name="hibernate.connection.password">password</property>
<property name="hibernate.connection.pool_size">10</property>
<property name="show_sql">true</property>
<property name="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</property>
<property name="hibernate.current_session_context_class">thread</property>

<mapping class="org.desenvolvimento.aberto.Carros" />
<mapping class="org.desenvolvimento.aberto.CarrosDetalhes" />

</session-factory>
</hibernate-configuration>

Na maioria das vezes é útil utilizar componentes de listas conhecidos como Combobox ou Dropdown para mostrar relacionamentos de dados entre tabelas e de uma forma amigável exibir a descrição dos itens de dados e manipular seu identificador único. Para isto o Web Dynpro nos disponibiliza componentes e classes no qual nos permite manipular dados através de objetos e persisti-los em um banco de dados relacional usando o Open SQL ou SAP Persistence Service. Um relacionamento de dados mais simples é criado através de duas tabelas, sendo uma tabela pai que contem os dados mestres e uma filho que contem um ou mais identificadores relacionados ao pai. Um modo de fácil de identificar e tradicional de utilizar relacionamentos em um banco de dados é através de chaves estrangeiras, uma chave estrangeira é um campo, que aponta para a chave primária de outra tabela ou da mesma tabela. Ou seja, passa a existir uma relação entre duas tabelas ou de uma única tabela. A finalidade da chave estrangeira é garantir a integridade dos dados referenciais, pois apenas serão permitidos valores que supostamente vão aparecer na base de dados e estão ligados ao registro mestre.

Foreign Key: Chaves Estrangeiras

Exibindo Relacionamentos

1 – Crie uma nova tabela transparente para os tipos de cargos e a ative, use a imagem abaixo para referencia:

Tabela Transparente - ZTCargo

Tabela Transparente – ZTCargo

2 – Crie uma nova tabela transparente para os participantes e inclua o campo para referencia chamado CARGOID da tabela cargos criada logo acima. Após ativar a tabela clique no campo de referencia para o CARGOID e clique no ícone (chave amarela) Foreign Key localizado na barra de ferramenta da sua tabela para criar um relacionamento entre as tabelas:

ZParticipante - Referencias

ZParticipante – Referencias

3 – Relacione os campos identificadores das duas tabelas:

Foreign Key - Chave Estrangeira

Foreign Key – Chave Estrangeira

4 – Assim que salvar e ativar sua tabela, clique na aba Entry Help/Check para ver a ligação entre as tabelas:

FK - Check

FK – Check

5 – Abra a transação SE16 e crie novos registros para a tabela de cargo:

Cargos - Registros

Cargos – Registros

6 – Caso queira pode criar um modelo de dados no Data Modeler para seus relacionamentos:

Data Modeler - Modelo de Dados

Data Modeler – Modelo de Dados

7 – Crie um componente Web Dynpro e no contexto do Controller crie um node para a tabela de participante e um node para a tabela de cargo e os importe para o contexto da sua View. No node para o cargo escolha a propriedade Cardinality como o valor 0..n. Use os elementos web adequados para cada tipo de campo de sua tabela, para o relacionamento use o elemento DropDownByIndex. Use a figura abaixo como referencia:

Node - Cardinality

Node – Cardinality

8 – Use os códigos abaixo para os respectivos métodos, salve, ative seu componente e crie sua aplicação:

Web Dynpro - Métodos

Web Dynpro – Métodos

9 – Digite os valores para cada tipo de campo e clique no botão de enviar:

Web Dynpro - Aplicação

Web Dynpro – Aplicação

10 – Você pode conferir os valores dos dados e o conteúdo do identificador do relacionamento entre as tabelas:

Data Browser = SE16

Data Browser  –  SE16

Exemplo:

Neste exemplo criamos duas tabelas no banco de dados SAP MAxDB através do dicionário de dados ABAP e as relacionamos através da chave primaria e uma chave estrangeira. Usamos um elemento Web Dynpro para tornar este relacionamento amigável, exibindo assim a descrição do relacionamento mas manipulando seu identificador e os persistindo através do SAP Persistence Service.

ABAP

ONACTIONACAO_BOTAO_ENVIAR

method ONACTIONACAO_BOTAO_ENVIAR .

* // Gravar dados
wd_this->ONGRAVAR( ).

endmethod.

WDDOINIT

method WDDOINIT .

* // Recupera dados do contexto
  Data: context_node type ref to if_wd_context_node.

* // Cria tabela interna do tipo da tabela do node
  Data: it_cargo type STANDARD TABLE OF if_main=>element_ZTCARGO,
        wa_cargos like line of it_cargo.

* // Seleciona dados da tabela Pai
  select * from ztcargo
    into table it_cargo.

* // Recupera node do contexto
  context_node = wd_context->get_child_node( name = 'ZTCARGO').

* // liga tabela interna a tabela do node
  context_node->BIND_TABLE( it_cargo ).

endmethod.

ONGRAVAR

method ONGRAVAR .

* // Recupera dados do controlador
  DATA lo_nd_usuario_controler TYPE REF TO if_wd_context_node.

  DATA lo_el_usuario_controler TYPE REF TO if_wd_context_element.
  DATA ls_usuario_controler TYPE wd_this->Element_usuario_controler.
  DATA lv_usuario_login TYPE wd_this->Element_usuario_controler-usuario_login.

  lo_nd_usuario_controler = wd_context->get_child_node( name = wd_this->wdctx_usuario_controler ).
  lo_el_usuario_controler = lo_nd_usuario_controler->get_element( ).

*   // Recupera Nome
  lo_el_usuario_controler->get_attribute(
    EXPORTING
      name =  `USUARIO_LOGIN`
    IMPORTING
      value = lv_usuario_login ).

* // Recupera Sobrenome
  DATA lv_sobrenome TYPE wd_this->Element_usuario_controler-sobrenome.
  lo_nd_usuario_controler = wd_context->get_child_node( name = wd_this->wdctx_usuario_controler ).
  lo_el_usuario_controler = lo_nd_usuario_controler->get_element( ).

  lo_el_usuario_controler->get_attribute(
    EXPORTING
      name =  `SOBRENOME`
    IMPORTING
      value = lv_sobrenome ).

* // Recupera Salario
  DATA lv_salario TYPE wd_this->Element_usuario_controler-salario.
  lo_nd_usuario_controler = wd_context->get_child_node( name = wd_this->wdctx_usuario_controler ).
  lo_el_usuario_controler = lo_nd_usuario_controler->get_element( ).

  lo_el_usuario_controler->get_attribute(
    EXPORTING
      name =  `SALARIO`
    IMPORTING
      value = lv_salario ).

* // Recupera Data
  DATA lv_data TYPE wd_this->Element_usuario_controler-data.
  lo_nd_usuario_controler = wd_context->get_child_node( name = wd_this->wdctx_usuario_controler ).
  lo_el_usuario_controler = lo_nd_usuario_controler->get_element( ).

  lo_el_usuario_controler->get_attribute(
    EXPORTING
      name =  `DATA`
    IMPORTING
      value = lv_data ).

* // Recupera Genero
  DATA lv_genero TYPE wd_this->Element_usuario_controler-genero.
  lo_nd_usuario_controler = wd_context->get_child_node( name = wd_this->wdctx_usuario_controler ).
  lo_el_usuario_controler = lo_nd_usuario_controler->get_element( ).

  lo_el_usuario_controler->get_attribute(
    EXPORTING
      name =  `GENERO`
    IMPORTING
      value = lv_genero ).

* // Recupera Ativo
  DATA lv_ativo TYPE wd_this->Element_usuario_controler-ativo.
  lo_nd_usuario_controler = wd_context->get_child_node( name = wd_this->wdctx_usuario_controler ).
  lo_el_usuario_controler = lo_nd_usuario_controler->get_element( ).

  lo_el_usuario_controler->get_attribute(
    EXPORTING
      name =  `ATIVO`
    IMPORTING
      value = lv_ativo ).

* // Recupera Observacao

  DATA lv_observacao TYPE wd_this->Element_usuario_controler-observacao.
  lo_nd_usuario_controler = wd_context->get_child_node( name = wd_this->wdctx_usuario_controler ).
  lo_el_usuario_controler = lo_nd_usuario_controler->get_element( ).

  lo_el_usuario_controler->get_attribute(
    EXPORTING
      name =  `OBSERVACAO`
    IMPORTING
      value = lv_observacao ).

* // Retorna valores do dropdown by index

* // Retorna conexto do node
  DATA: context_node type ref to if_wd_context_node.

* // Cria tabela baseado no tipo do node
  DATA: it_cargo type STANDARD TABLE OF if_main=>element_ZTCARGO,
        wa_cargo like line of it_cargo,
        index type i.

* // chama o node requerido
  context_node = wd_context->get_child_node( name = 'ZTCARGO').

* // Recupera o indice selecionado
  index =  context_node->GET_LEAD_SELECTION_INDEX( ).

* // Recupera os valores atraves do indice
  context_node->GET_STATIC_ATTRIBUTES(
     exporting index = index
     importing STATIC_ATTRIBUTES = wa_cargo ).

* // Declara variáveis
  DATA : guid         TYPE zGuid,
         nome         TYPE ZNome,
         sobrenome    TYPE ZSobrenome,
         cargo        TYPE ZCARGOID,
         salario      TYPE Zsal,
         data_adm     TYPE Zdata,
         genero       TYPE ZGenero,
         ativo        TYPE Zativo,
         observacao   TYPE ZObservacao.

* // Alimenta dados.

  nome       = LV_USUARIO_LOGIN.
  sobrenome  = LV_SOBRENOME.
  cargo      = WA_CARGO-CARGOID.
  salario    = LV_SALARIO.
  data_adm   = LV_DATA.
  genero     = LV_GENERO.
  ativo      = LV_ATIVO.
  observacao = LV_OBSERVACAO.

* // Gera GUID

  CALL FUNCTION 'GUID_CREATE'
    IMPORTING
      ev_guid_16 = guid.

* // Cria persistencia
* // Declara classe Actor e Persistencia
  DATA:  agente       TYPE REF TO ZCA_PARTICIPANTE,
         participante TYPE REF TO ZCL_PARTICIPANTE.

* // Cria agente da classe actor
  AGENTE = ZCA_PARTICIPANTE=>AGENT.

* // cria persistencia
  try.
      participante = AGENTE->CREATE_PERSISTENT( guid ).
    catch CX_OS_OBJECT_EXISTING.
  ENDTRY.

* // Alimenta objeto de persistencia
  PARTICIPANTE->SET_NOME( NOME ).
  PARTICIPANTE->SET_SOBRENOME( SOBRENOME ).
  PARTICIPANTE->SET_CARGOID( CARGO ).
  PARTICIPANTE->SET_DATA( DATA_ADM ).
  PARTICIPANTE->SET_SALARIO( SALARIO ).
  PARTICIPANTE->SET_GENERO( GENERO ).
  PARTICIPANTE->SET_ATIVO( ATIVO ).
  PARTICIPANTE->SET_OBSERVACAO( OBSERVACAO ).

* // Grava e finaliza persistencia
  TRY.
    COMMIT WORK.

* // Exibe mensagem
    data lo_api_controller     type ref to if_wd_controller.
    data lo_message_manager    type ref to if_wd_message_manager.

    lo_api_controller ?= wd_This->Wd_Get_Api( ).

* // Chama método de menssagem
    CALL METHOD lo_api_controller->GET_MESSAGE_MANAGER
      RECEIVING
        MESSAGE_MANAGER = lo_message_manager.

* // Exibe mensagem
    CALL METHOD lo_message_manager->REPORT_SUCCESS
      EXPORTING
        MESSAGE_TEXT = 'Dados Inseridos com Sucesso'.

  ENDTRY.

endmethod.