Jape (Persistência de dados)

Introdução

O Jape é o framework de persistência de dados da Sankhya, ele disponibiliza métodos para consulta, inserção, deleção e alteração de registros em instâncias de dados previamente mapeadas. Na maioria dos casos ele é o principal recurso para manipulação de dados no banco, mas também é possível usar querys nativas (NativeSql) para essa finalidade.

O Jape provê recursos para mapeamento de classes Java em tabelas de banco de dados relacionais, além de recursos de consulta e manipulação de dados, evitando que o desenvolvedor precise realizar essa tarefas diretamente.

1344

Camadas da aplicação.

Referência Técnica (Javadoc)

Consulte a especificação de interfaces, classes, construtores, métodos e atributos do Jape:

Controle de sessão

A sessão é um estado de comunicação entre a aplicação e o banco de dados, e consiste em requisições e respostas realizada entre os dois. A sessão é usada para estabelecer uma conexão com o banco de dados, onde os objetos de sessão são projetados para serem instanciados sempre que haja um interação necessária com o banco. Os objetos de sessão não devem ser mantidos constantemente abertos, eles devem ser criados e fechados conforme necessário. A principal função da sessão é fornecer um estado que permita operações de criação, leitura e exclusão de dados em instâncias de entidades mapeadas. O controle se inicia com a sua abertura e é finalizado com o encerramento da sessão.

Abertura da sessão
A abertura da sessão é realizada pelo método JapeSession.open(), sempre que uma sessão é aberta se vincula a um objeto SessionHandle, que gerencia metadados da sessão. A sessão deve ser aberta usando o bloco Try e para fechamento da sessão o bloco Finally .

Fechamento da sessão
O fechamento da sessão é necessário para liberar os recursos atrelados a ela, esse procedimento é realizado com o método JapeSession.close(hnd).

Uma sessão típica deve adotar o seguinte padrão:

SessionHandle hnd = null; // identifica o início de uma sessão. 

try {
	hnd = JapeSession.open(); // abertura da sessão

	hnd.execWithTX( new JapeSession.TXBlock(){
		public void doWithTx() throws Exception{
		
			//realiza operações em entidades ou NativeSQL
			
		}
	});
} finally {
	JapeSession.close(hnd); // fechamento da sessão
}

No exemplo acima foi criado o objeto hnd do tipo SessionHandler acima do bloco try, permitindo assim fechar a sessão sempre que a thread sair do bloco.

Timeout
Especifica o limite de tempo para expiração da sessão, além de evitar que uma sessão cause algum tipo de conflito ou algum deadlock no banco de dados. O TimeOut de sessão pode ser evitado usando métodos específicos.

  • setCanTimeout(false) - seta o valor false para que o timeout não ocorra.
SessionHandle hnd = null;

try {
	hnd = JapeSession.open();
  hnd.setCanTimeout(false); //instrução para evitar o Timeout

	hnd.execWithTX( new JapeSession.TXBlock(){
		public void doWithTx() throws Exception{
		
			//realiza operações em entidades ou NativeSQL
			
		}
	});
} finally {
	JapeSession.close(hnd); // fechamento da sessão
}

Controle de transação

Uma transação consiste em uma sequencia de operações realizadas no banco de dados, é considerada uma unidade indivisível de trabalho, onde todas ou nenhuma das ações serão executadas. A transação é associada a uma sessão e têm dois objetivos principais:

  • Fornecer operações no banco de dados, mantendo a integridade dos dados mesmo em casos de falhas.

  • Fornecer isolamento entre operações executados sobre o banco de dados.

1740

Isolamento em transações

Quando a transação é finalizada com sucesso, as modificações correspondentes são salvas (commit).

1293

Commit em uma transação

Porém se uma operação da transação falhar, toda a unidade de trabalho é revertida (rollback)

1190

Rollback em transações

No Jape há transações automáticas e manuais. A primeira usada para todos os modelos de transação e a segunda em operações curtas no banco de dados (onde cada interação é independente).

Transação Automática

A transação automática é utilizada para qualquer tipo de operação no banco de dados. Ela precisa da anotação @ejb.transaction type="Required", no caso de um serviço.

/**
 * @ejb.transaction type="Required"
 */

Transação Manual

A transação manual foi criada para situações de curta operação no banco de dados, onde cada interação é independente. Transações manuais devem adotar a seguinte estrutura:

SessionHandle hnd = null;

try {
	hnd = JapeSession.open();

	hnd.execWithTX( new JapeSession.TXBlock(){
		public void doWithTx() throws Exception{
		
			//realiza operações em entidades ou NativeSQL
			
		}
	});
} finally {
	JapeSession.close(hnd);
}

Algumas propriedades podem ser associadas à sessão no momento da transação, como as listadas abaixo:

JapeSession.putProperty("usuario_logado", CODUSU);
JapeSession.putProperty("emp_usu_logado", usuVO.getCODEMP());
JapeSession.putProperty("dh_atual", new Timestamp(System.currentTimeMillis()));
JapeSession.putProperty("d_atual", new Timestamp(TimeUtils.getToday()));
JapeSession.putProperty("usuarioVO", usuVO);
JapeSession.putProperty("authInfo", authInfo);
JapeSession.putProperty("modelSession",ModelSessionManager.getSingleton().getSession(authInfo.getAuthenticationID()));

Acesso a dados

O acesso a dados compreende a capacidade do usuário de acessar ou recuperar dados armazenados no banco de dados. O JAPE foi projetado para efetuar esse processo de maneira simples e segura, garantindo assim a integridade dos dados consultados.

Após a abertura de sessão e conexão com o banco de dados inicia-se a coleta de informações. Nessa operação é instanciado um objeto que será o mapeamento do banco de dados feito do dicionário de dados, com o auxilio do método DWF Facade que se define a instância que irá ser trabalhada.

Dependendo da conexão que o usuário fizer, além da sessão é necessário abrir a conexão com o JDBC (iniciando uma sessão). Isso é sempre realizado quando for feito alguma alteração com o banco de dados.

Abaixo são listados métodos para acesso e coleta de dados:

getDefaultValueObjectInstance

Método utilizado para criar um DynamicVO vazio (default). Retorna um DynamicVO para a instância definida. O parâmetro utilizado é apenas o nome da instância.

EntityFacade dwfFacade = EntityFacadeFactory.getDWFFacade();
DynamicVO finVO = (DynamicVO) dwfFacade.getDefaultValueObjectInstance(DynamicEntityNames.FINANCEIRO);

findEntityByPrimaryKeyAsVO

Método utilizado para buscar um registro pela PK. Retorna um DynamicVO para a instância pesquisada. Os parâmetros utilizados são: Nome da instância e um array de objetos com os parâmetros (PK). A ordem dos parâmetros deve ser exatamente a mesma da primakey definida na tabela.

EntityFacade dwfFacade = EntityFacadeFactory.getDWFFacade();
DynamicVO finVO = (DynamicVO) dwfFacade.findEntityByPrimaryKeyAsVO(DynamicEntityNames.FINANCEIRO, new Object[]{nuFin});

findByDynamicFinderAsVO

Método utilizado para buscar uma coleção de registros por um critério personalizado. Retorna uma coleção de DynamicVO. Os parâmetros utilizados são: Nome da instância, critério SQL e um array de objetos com os parâmetros. A ordem dos parâmetros deve ser a mesma utilizada no critério.

EntityFacade dwffacade = EntityFacadeFactory.getDWFFacade();
FinderWrapper finder = new FinderWrapper(DynamicEntityNames.FINANCEIRO, "this.CODPARC = ? AND this.DTVENC > ?", new Object[]{codParc, dtVenc});
Collection<DynamicVO> financeiros = dwfFacade.findByDynamicFinderAsVO(finder);

createEntity

Método utilizado para criar um registro. Retorna um PersistentLocalEntity. Os parâmetros utilizados são: Nome da instância e o DynamicVO correspondente. Este método requer uma transação ativa (controlada pelo container ou manual).

EntityFacade dwfFacade = EntityFacadeFactory.getDWFFacade();

DynamicVO finVO = (DynamicVO) dwfFacade.getDefaultValueObjectInstance(DynamicEntityNames.FINANCEIRO);
finVO.setProperty("DTNEG", dtNeg);

finVO.setProperty("DTVENC", dtVenc);

finVO. setProperty("CODEMP", codEmp);

finVO. setProperty("CODPARC", codParc);

finVO. setProperty("CODTIPOPER", codTipOper);

FfinVO. setProperty("CODTIPTIT", codTipTit);

finVO. setProperty("CODCTABCO", codCtaBco);

finVO. setProperty("VLRDESDOB", vlrDesdob);
dwfFacade.createEntity(DynamicEntityNames.FINANCEIRO, (EntityVO) finVO);

setValueObject

Método utilizado para alterar um registro existente. O parâmetro utilizado é o DynamicVO correspondente. Este método requer uma transação ativa (controlada pelo container ou manual).

EntityFacade dwfFacade = EntityFacadeFactory.getDWFFacade();

PersistentLocalEntity finEntity = dwfFacade.findEntityByPrimaryKey (DynamicEntityNames.FINANCEIRO, new Object[]{nuFi});
DynamicVO finVO = (DynamicVO) finEntity.getValueObject();

finVO. setProperty("CODTIPTIT", codTipTit);

finVO.setProperty("DTVENC", dtVenc);

finEntity.setValueObject((EntityVO) finvo);

removeEntity

Método utilizado para remover um registro pela PK. Os parâmetros utilizados são: Nome da instância e um array de objetos com os parâmetros (PK). A ordem dos parâmetros deve ser exatamente a mesma da primakey definida na tabela. Este método requer uma transação ativa (controlada pelo container ou manual).

EntityFacade dwfFacade = EntityFacadeFactory.getDWFFacade();
dwfFacade.removeEntity(DynamicEntityNames.FINANCEIRO, new Object[{nuFin}];

removeByCriteria

Método utilizado para remover registros por um critério personalizado. Retorna a quantidade de registros removidos. Os parâmetros utilizados são: Nome da instância, critério SQL e um array de objetos com os parâmetros. A ordem dos parâmetros deve ser a mesma utilizada no critério. Este método requer uma transação ativa (controlada pelo container ou manual).

EntityFacade dwfFacade = EntityFacadeFactory.getDWFFacade();
FinderWrapper finder = new FinderWrapper(DynamicEntityNames.FINANCEIRO, "this.CODPARC = ? AND this.DTIVENC >= ?", new Object[]{codParc, dtVenc});
dwfFacade. removeByCriteria(finder);
  • Usando JapeWrapper
SessionHandle hnd = null;
try {
    hnd = JapeSession.open();
    JapeWrapper empresaDAO = JapeFactory.dao(DynamicEntityNames.EMPRESA);
    DynamicVO empresa = empresaDAO.findByPK(BigDecimal.ZERO);
}
finnaly {
    JapeSession.close(hnd);
}
  • Usando NativeSQL
NativeSql sql = new NativeSql(jdbc);
sql.appendSql("SELECT * FROM TSIUSU WHERE CODUSU = :CODUSU");
sql.setNamedParameter("CODUSU", BigDecimal.ZERO); 
ResultSet rset = sql.executeQuery();

Entity listener

Os listeners são regras associadas ao Jape pelo mapeamento das entidades e são realizados no dicionário de dados. Cada listener é executado mediante a um evento de inclusão, alteração ou remoção de registro em uma tabela. Os listeners são controlados pelo JAPE e com ele há total controle da sessão e transação, ou seja o usuário tem acesso direto ao dado requisitado ao banco.

Em geral associa-se um listener a cada tabela, mas há casos em que são feitos mais de uma associação, porém não é um processo comum.

466

Entity Listener

Os métodos que podem ser implementados são definidos na interface PersistenceEventListener.java, sendo eles:

public interface PersistenceEventListener extends Serializable {
    void afterDelete(PersistenceEvent event) throws Exception; // Executado  depois de remover o registro.
    void afterInsert(PersistenceEvent event) throws Exception; // Executado  depois de incluir o registro.
    void afterloadValueObject(PersistenceEvent event) throws Exception; // Executado depois de carregar um VO. Em casos de entidades de somente leitura (readOnly), será executado uma vez por registro em cada transação.
    void afterRetrieveValueObject(PersistenceEvent event) throws Exception; // Executado depois de carregar um VO.
    void afterUpdate(PersistenceEvent event) throws Exception; // Executado  depois de alterar o registro.
    void beforeDelete(PersistenceEvent event) throws Exception; // Executado antes de remover o registro.
    void beforeInsert(Persistencefvent event) throws Exception; // Executado  antes de incluir o registro.
    void beforeUpdate(PersistenceEvent event) throws Exception; // Executado  antes de alterar o registro.
}

Para adicionar um listener em uma entidade do sistema, é necessário criar um mapeamento no arquivo correspondente à entidade, no dicionário de dados. A classe listener deve extender a classe abstrata PersistenceEventAdapter, que por sua vez implementa a interface PersistenceEventListener. A imagem abaixo representa o funcionamento do listener associado à entidade Beneficio.

1900

Entity listener

1 - Entidade que está sendo "ouvida" pelo listener.
2 - BeneficioListener.java

public class BeneficioListener extends PersistenceEventAdapter{
	
	private static final long serialVersionUID = 1l;

	@Override
	public void beforeInsert(PersistenceEvent event) throws Exception {
		validaBeneficio((DynamicVO) event.getVo());
	}
	
	@Override
	public void beforeUpdate(PersistenceEvent event) throws Exception {
		validaBeneficio((DynamicVO) event.getVo());
	}
	
	private void validaBeneficio(DynamicVO benVO) throws Exception {
		if (benVO.asInt("CODEVENTO") == benVO.asInt("CODEVENTOEMP")) {
			throw (Exception) SKError.registry(TSLevel.ERROR, "BEN_E00001", new Exception("O cdigo do evento do beneficirio no pode ser igual ao cdigo evento da empresa."));
		}
	}
}

3 - Beneficio.xml

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE entity>
<entity name="Beneficio" main-object="Beneficio" commit-type="D">
   <runtime max-results="50000" timeout="20" load-on-find="true" />
   <event-listener class="br.com.sankhya.beneficio.model.entitylisteners.BeneficioListener" />   
</entity>

4 - Eventos de inclusão e atualização de registros na tabela disparam a execução do listener.

Para mais informações sobre como implementar listeners em extensões, acesse o artigo Recursos em extensões.

## Como tirar dúvidas?

Para tirar dúvidas e compartilhar informações, use a sala Jape da comunidade Sankhya Developer.