Database – Triggers – Oracle – IBM DB2 – Microsoft SQL Server – Java

Publicado: 15 de julho de 2014 em Java

Uma Trigger ou gatilho é um tipo especial de procedimento armazenado (Stored Procedure) que executa automaticamente quando um evento ocorre no servidor de banco de dados. Gatilhos DML executam quando um usuário tenta modificar dados através de uma linguagem de manipulação de dados (DML). Eventos DML são INSERT, UPDATE, ou DELETE em uma tabela ou exibição. Esses gatilhos disparam quando qualquer evento válido é acionado, independentemente de haver ou não linhas da tabela afetadas.

As Triggers possuem muitas diferenças de sintaxe e funcionalidades de um banco de dados para outro, por exemplo Trigger que dispara o evento INSTEAD OF INSERT (ao invés de inserir) no banco de dados Oracle só pode ser declarada sobre uma VIEW enquanto em MSSQL Server pode ser declarada em uma tabela comum, o evento BEFORE INSERT (antes de inserir) existe nos bancos de dados Oracle e IBM DB2, mas não existe no MSSQL. Por este motivo é recomendado que você procure os links oficiais para saber mais detalhes sobre as Triggers.

Triggers - Oracle - DB2 - MSSQL

Triggers – Oracle – DB2 – MSSQL

 

Algo extremamente útil que você deve saber sobre Triggers

As Triggers foram regulamentadas na revisão da língua SQL em 1999 e passou a ser padrão em banco de dados relacionais, no entanto alguns bancos de dados vem atualizando estes recursos regularmente, por exemplo o Oracle utiliza Triggers que disparam por esquema (Schema-level triggers) desde a sua versão 9i, enquanto o MSSQL suporta trigger do tipo DDL, ou logon trigger apenas desde a versão 2008.

Muitos sistemas de grande porte como SAP e vários outros não utilizam triggers, principalmente sistemas que no qual rodam em diversos bancos de dados, estas são consideradas verdadeiras armadilhas por muitos DBAs, podem ser facilmente esquecidas na hora de alterações e se tornarem invalidas (Oracle), gerando assim muitos erros caso não haja uma boa politica de desenvolvimento e qualidade (testes unitários e integrados) antes de colocar as rotinas do sistema em produção. Mesmo as IDEs de estudios SQL já mudaram varias vezes a localização do node de triggers ao longo de suas versões para tentar facilitar a manutenção das mesmas, aqui veremos como exemplo a IDE do IBM Data Studio que possui um wizard especifico para triggers.

As Triggers também devem ser desenvolvidas com cuidado sempre seguindo as melhores praticas, pois o desenvolvimento SQL é muito abrangentes e cheio de recursos, então é fácil extrapolar e criar triggers contendo cursores, stored procedures, rollbacks, acessos repetitivos, outras triggers, lembre-se da lei de Murphy, tudo que não pode ser escrito em uma trigger, será e isto reduz sensivelmente a performance de um sistema, sem contar com a complexidade do desenvolvimento, transformado seu desenvolvimento SQL em verdadeiros planos da ACME, lembre-se do coite que tentava incessantemente capturar o papa-léguas.

Cuidado com as Triggers

Cuidado com as Triggers

Nunca use Triggers para criar campos auto incremento para os códigos (ID), os bancos de dados possuem recursos específicos para este trabalho, o uso de triggers somente criará acessos e UPDATES desnecessários no banco de dados, além de reduzir a performance, imagine importar 10.000 linhas utilizando um Insert…Select, à partir de uma outra tabela ou arquivos externos, você efetuara um Select e um Update para cada registro se utilizar triggers, enquanto os bancos de dados contam com seus campos auto incremento que já nos permitem utilizar dados em massa e inserir as 10.000 linhas em um único acesso.

As Triggers também são um alto risco a segurança, é possível escalar privilégios e se tornar proprietário do banco de dados e do servidor SQL caso você insira um código mal intencionado em uma Trigger, exemplo: suponha que o usuário Jõao da Silva tenha diretos de Administrador do banco de dados,  se um desenvolvedor mal intencionado criar uma trigger DDL sobre o evento DELETE de uma tabela qualquer utilizando por exemplo a instrução: GRANT CONTROL SERVER TO BadUser; Quando o usuário João da Silva executar o procedimento que dispara a trigger o Grant será delegado ao usuário mal intencionado e os direitos aplicados a ele, enquanto ele mesmo não teria acesso para delegar tal direito, deste modo possibilitando que o desenvolvedor mal intencionado tome conta do servidor SQL, podendo fazer o que bem entender com os dados.

Security: http://msdn.microsoft.com/pt-br/library/ms191134.aspx

IBM Data Studio

O IBM Data Studio possui uma funcionalidade especifica para criar triggers (Wizard), assim como possui para Stored Procedures e Function no qual necessitam de executar um Deploy antes de ser executada. Portanto você não poderá criar a Trigger utilizando a configuração Default do editor SQL comum, para isto você precisa mudar a configuração do terminador de instrução de ponto e virgula (;) para arroba(@), basta clicar com o botão direito do mouse e escolher a opção: Configurar Terminador de Instrução. Utilize a figura abaixo para referencia:

IBM Data Studio - Configuração

IBM Data Studio – Configuração

Exemplo:

Neste exemplo criamos uma funcionalidade similar do nosso exemplo anterior (Cursor), utilizando a mesma modelagem de dados, porem para executar este procedimento as tabelas DESCONTO e SALARIO devem estar vazias, o usuário entrará com o código do funcionário e a porcentagem do desconto e a trigger sobre a tabela DESCONTO se encarregara de criar o lançamento na tabela de SALARIO.

SQL

Oracle

create table Funcionarios(
  ID_Funcionario  NUMBER(5),
  Nome            VARCHAR2(30),
  Sobrenome       VARCHAR2(70),
  Cargo           VARCHAR2(30),
  Salario         NUMBER(9,2));

-- Cria Funcionarios
Insert into FUNCIONARIOS values (1,'Steve','Gates','Programador',2550.56);
Insert into FUNCIONARIOS values (2,'Bill','Jobs','Diretor',5143.71);
Insert into FUNCIONARIOS values (3,'Wozniak','Gates','Desenvolvedor', 4389.21);

-- Cria tabela com a porcentagem de descontos
Create table DESCONTO (
  ID_FUNCIONARIO NUMBER,
  PORCENTAGEM NUMBER(9,2));

-- Cria tabela de lançamentos de descontos
Create table SALARIO (
  ID_FUNCIONARIO NUMBER,
  DATA_LANC  DATE,
  VDESCONTO NUMBER(9,2));

-- Deleta dados antigos
-- Caso utlizou exemplos anteriores
delete from desconto;
delete from salario;

-- Cria trigger na tabela Desconto
create or replace TRIGGER DESCONTO_INSERT
   BEFORE INSERT ON DESCONTO
   FOR EACH ROW

   -- Declara variáveis
   DECLARE pID NUMBER;
           pSalario NUMBER(9,2);
           pPorcentagem NUMBER(9,2);
   BEGIN   

    -- Alimenta variáveis com os valores a serem inseridos
    pID := :new.ID_FUNCIONARIO;
    pPorcentagem := :new.PORCENTAGEM;

    -- Seleciona Salario do funcionario corrente
    Select SALARIO INTO pSalario FROM FUNCIONARIOS
    WHERE ID_FUNCIONARIO = pID;

    -- insere na tabela de lançamentos de salario
    Insert into SALARIO
     values (
             pID,
             SYSDATE,
             (pSalario * pPorcentagem)/100);

   END DESCONTO_INSERT;

DB2

create table Funcionarios (
    ID_Funcionario  INTEGER,
    Nome            VARCHAR(30),
    Sobrenome       VARCHAR(70),
    Cargo           VARCHAR(30),
    Salario         NUMERIC(9,2))@

Insert into FUNCIONARIOS values (1,'Steve','Gates','Programador',2550.56)@
Insert into FUNCIONARIOS values (2,'Bill','Jobs','Diretor',5143.71)@
Insert into FUNCIONARIOS values (3,'Wozniak','Gates','Desenvolvedor', 4389.21)@

-- Cria tabela com a porcentagem de descontos
Create table DESCONTO (
  ID_FUNCIONARIO INTEGER,
  PORCENTAGEM DECIMAL(9,2))@

-- Cria tabela de lançamentos de descontos
Create table SALARIO (
  ID_FUNCIONARIO INTEGER,
  DATA_LANC  DATE,
  VDESCONTO DECIMAL(9,2))@

-- Deleta dados antigos
-- Caso utlizou exemplos anteriores
delete from desconto@
delete from salario@

-- Cria trigger na tabela Desconto
create TRIGGER DESCONTO_INSERT
   BEFORE INSERT ON DESCONTO
   REFERENCING NEW AS N
   FOR EACH ROW

 P1:  BEGIN

   -- Declara variáveis
   DECLARE pID NUMBER;
   DECLARE pSalario DECIMAL(9,2);
   DECLARE pPorcentagem DECIMAL(9,2);

    -- Alimenta variáveis com os valores a serem inseridos
    SET pID = N.ID_FUNCIONARIO;
    SET pPorcentagem = N.PORCENTAGEM;

    -- Seleciona Salario do funcionario corrente
    Select SALARIO INTO pSalario FROM FUNCIONARIOS
    WHERE ID_FUNCIONARIO = pID;

    -- insere na tabela de lançamentos de salario
    Insert into SALARIO
     values (
             pID,
             SYSDATE,
             (pSalario * pPorcentagem)/100);

   END P1

MSSQL

create table Funcionarios (
   ID_Funcionario  Int,
   Nome            VARCHAR(30),
   Sobrenome       VARCHAR(70),
   Cargo           VARCHAR(30),
   Salario         Decimal(9,2));

Insert into FUNCIONARIOS values (1,'Steve','Gates','Programador',2550.56);
Insert into FUNCIONARIOS values (2,'Bill','Jobs','Diretor',5143.71);
Insert into FUNCIONARIOS values (3,'Wozniak','Gates','Desenvolvedor', 4389.21);

-- Cria tabela com a porcentagem de descontos
Create table DESCONTO (
  ID_FUNCIONARIO INT,
  PORCENTAGEM DECIMAL(9,2));

-- Cria tabela de lançamentos de descontos
Create table SALARIO (
  ID_FUNCIONARIO INT,
  DATA_LANC  DATE,
  VDESCONTO DECIMAL(9,2));

-- Deleta dados antigos
-- Caso utlizou exemplos anteriores
delete from desconto;
delete from salario;

-- Cria trigger na tabela Desconto
create TRIGGER DESCONTO_INSERT ON
  DESCONTO AFTER INSERT AS      

   BEGIN
   -- Declara variáveis
   DECLARE @pID Int,
           @pSalario DECIMAL(9,2),
           @pPorcentagem DECIMAL(9,2); 

    -- Alimenta variáveis com os valores a serem inseridos
	Select @pID = ID_FUNCIONARIO,
	       @pPorcentagem = PORCENTAGEM
    from inserted;

    -- Seleciona Salario do funcionario corrente
    Select  @pSalario = SALARIO FROM FUNCIONARIOS
    WHERE ID_FUNCIONARIO = @pID;

    -- insere na tabela de lançamentos de salario
    Insert into SALARIO
     values (
             @pID,
             GETDATE(),
             (@pSalario * @pPorcentagem)/100);

   END;

Java

import java.awt.Container;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Vector;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.table.DefaultTableModel;

public class Cursor implements ActionListener, ItemListener {
	// Cria componentes
	private JTable tabela;
	private JRadioButton banco1;
	private JRadioButton banco2;
	private JRadioButton banco3;
	private JButton botao;
	private JLabel lid_funcionario;
	private JLabel lporcentagem;
	private JTextField tid_funcionario;
	private JTextField tporcentagem;

	// Declara objetos de conexão
	private static Connection conn;
	private static Statement query;
	private static String bancodedados;

	public void conectar(String login, String senha) {

		// Verifica strings de conexão

		// ORACLE
		if (bancodedados == "oracle") {
			try {
				// Define Driver de conexão JDBC thin
				Class.forName("oracle.jdbc.driver.OracleDriver");
				conn = DriverManager.getConnection(
						"jdbc:oracle:thin:@localhost:1521:xe", login, senha);

				// Executa pedido SQL
				query = conn.createStatement();

			}

			catch (ClassNotFoundException ex) {
				ex.printStackTrace();
			}

			catch (SQLException ex) {
				ex.printStackTrace();
			}
		}

		// DB2
		if (bancodedados == "db2") {
			try {
				// Define Driver de conexão JDBC
				Class.forName("com.ibm.db2.jcc.DB2Driver");
				conn = DriverManager.getConnection(
						"jdbc:derby:net://localhost:50000/deva", login, senha);

				// Executa pedido SQL
				query = conn.createStatement();
				// JOptionPane.showMessageDialog(menssagem,
				// "Conexão Efetuada com sucesso!");
			}

			catch (ClassNotFoundException ex) {
				ex.printStackTrace();
			}

			catch (SQLException ex) {
				// JOptionPane.showMessageDialog(menssagem, "Erro na conexão!");
				ex.printStackTrace();
			}

		}

		// MICROSOFT SQL SERVER
		if (bancodedados == "mssql") {
			try {
				// Define Driver de conexão JDBC
				String URL = "jdbc:sqlserver://localhost\\SQLEXPRESS:1433;databaseName=devaberto"
						+ ";user=" + login + ";password=" + senha;

				Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
				conn = DriverManager.getConnection(URL);

				// Executa pedido SQL
				query = conn.createStatement();

			}

			catch (ClassNotFoundException ex) {
				ex.printStackTrace();
			}

			catch (SQLException ex) {
				ex.printStackTrace();
			}

		}

	}

	public void executaSQL(String sql)   {
		// Cria nova instrução SQL
		Statement trigger;
		;
		try {
			trigger = conn.createStatement();
			trigger.execute(sql);
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public ResultSet retornaTabela() throws SQLException {
		// Cria nova instrução SQL
		Statement query;
		query = conn.createStatement();
		String sql;

		// Verfica banco de dados e passa script SQL
		sql = "Select A.ID_FUNCIONARIO, " + "A.NOME, " + " A.CARGO, "
				+ " A.SALARIO, " + "B.PORCENTAGEM, " + "C.VDESCONTO, "
				+ "C.DATA_LANC,  " + "A.SALARIO - C.VDESCONTO AS SLIQUIDO "
				+ "from FUNCIONARIOS A, DESCONTO B, SALARIO C " + "Where "
				+ "A.ID_FUNCIONARIO = B.ID_FUNCIONARIO  AND "
				+ "A.ID_FUNCIONARIO = C.ID_FUNCIONARIO";

		// Executa Script
		ResultSet dados = query.executeQuery(sql);

		// Retorna set de dados
		return dados;
	}

	// Modelo
	public static DefaultTableModel criaTableModel(ResultSet rs)
			throws SQLException {

		// Cria um modelo de tabela
		ResultSetMetaData metaData = rs.getMetaData();

		// Retorna as colunas
		Vector<String> colunas = new Vector<String>();

		int columnCount = metaData.getColumnCount();

		for (int column = 1; column <= columnCount; column++) {
			colunas.add(metaData.getColumnName(column));
		}

		// Retorna dados
		Vector<Vector<Object>> dados = new Vector<Vector<Object>>();

		while (rs.next()) {
			Vector<Object> vector = new Vector<Object>();

			for (int columnIndex = 1; columnIndex <= columnCount; columnIndex++) {
				vector.add(rs.getObject(columnIndex));
			}
			dados.add(vector);
		}

		return new DefaultTableModel(dados, colunas);

	}

	public void itemStateChanged(ItemEvent arg0) {

		// Verifica item banco de dados selecionado
		Object fonte = arg0.getItemSelectable();
		int estado = arg0.getStateChange();

		if (estado == arg0.SELECTED) {

			if (fonte == banco1) {
				bancodedados = "oracle";
			}

			if (fonte == banco2) {
				bancodedados = "db2";
			}

			if (fonte == banco3) {
				bancodedados = "mssql";
			}

		}

	}

	public void actionPerformed(ActionEvent arg0) {
		// Efetua login no banco de dados
		Cursor acesso = new Cursor();

		if (bancodedados == "oracle") {
			acesso.conectar("daberto", "p@55w0rd");
		}

		if (bancodedados == "db2") {
			acesso.conectar("db2admin", "p@55w0rd");
		}

		if (bancodedados == "mssql") {
			acesso.conectar("devaberto", "p@55w0rd");
		}

		try {
			// Executa Trigger (Tabela Desconto)

			acesso.executaSQL("insert into DESCONTO VALUES ("+ tid_funcionario.getText() +
					          ", " + tporcentagem.getText() + ")");

			// Executa Intrução SQL
			tabela.setModel(criaTableModel(acesso.retornaTabela()));
		} catch (SQLException e) {
			e.printStackTrace();
		}

	}

	public Container criaPainel() throws SQLException {
		// Cria painel principal
		JPanel painel = new JPanel();

		// Seleciona layout
		painel.setLayout(new BoxLayout(painel, BoxLayout.PAGE_AXIS));
		painel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));

		// Cria painel de escolha de conexão
		JPanel pescolha = new JPanel();

		pescolha.setLayout(new BoxLayout(pescolha, BoxLayout.LINE_AXIS));
		pescolha.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

		ButtonGroup grupo = new ButtonGroup();

		// Cria componentes de radio
		banco1 = new JRadioButton("Oracle");
		banco2 = new JRadioButton("IBM DB2");
		banco3 = new JRadioButton("Microsoft SQL");

		// Agrupa botões de radio
		grupo.add(banco1);
		grupo.add(banco2);
		grupo.add(banco3);

		// Cria ouvinte dos botões
		banco1.addItemListener(this);
		banco2.addItemListener(this);
		banco3.addItemListener(this);

		// Seleciona primeira conexão
		banco1.setSelected(true);

		// Adiciona botões ao painel
		pescolha.add(banco1);
		pescolha.add(banco2);
		pescolha.add(banco3);

		// Efetua primeira conexão ao banco (ORACLE)
		Cursor acesso = new Cursor();

		acesso.conectar("daberto", "myoracle123");

		// Cria modelo de tabela
		tabela = new JTable(criaTableModel(acesso.retornaTabela()));

		// Adiciona um painel de rolagem
		JScrollPane rolar = new JScrollPane(tabela);

		// Cria painel do botão
		JPanel pbotao = new JPanel(new GridLayout(0, 5, 10, 10));		

		// Cria botão
		lid_funcionario = new JLabel("Código Func:");
		lporcentagem = new JLabel("Porcentagem:");

		tid_funcionario = new JTextField();
		tporcentagem = new JTextField();

		botao = new JButton("Inserir");
		botao.addActionListener(this);

		// Define foco do cursor
		javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                tid_funcionario.requestFocus();
            }
        });

		// Adiciona botão ao painel
		pbotao.add(lid_funcionario);
		pbotao.add(tid_funcionario);
		pbotao.add(lporcentagem);
		pbotao.add(tporcentagem);
		pbotao.add(botao);

		// componentes ao painel principal
		painel.add(pescolha);
		painel.add(rolar);
		painel.add(Box.createVerticalStrut(10));
		painel.add(pbotao);

		return painel;
	}

	public static void criaGUI() {
		// Cria formulario
		JFrame formulario = new JFrame("Desenvolvimento Aberto - Triggers");
		formulario.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		// cria painel de conteudo
		Cursor acesso = new Cursor();

		try {
			formulario.setContentPane(acesso.criaPainel());
		}

		catch (SQLException e) {
			e.printStackTrace();
		}

		// Exibe o formulario
		formulario.setSize(700, 300);
		formulario.setVisible(true);
	}

	public static void main(String[] args) {
		javax.swing.SwingUtilities.invokeLater(new Runnable() {

			@Override
			public void run() {

				// Cria e mostra a GUI
				criaGUI();

			}
		});
	}
}
Anúncios

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

w

Conectando a %s