Arquivos de sites

JPA/Hibernate Static Metamodel Attributes não populados / nulos — Gerando NullPointerException

E ai galera, beleza?

Hoje tive um problema tentando utilizar os atributos metamodel estáticos do JPA/Hibernate,
sempre quando eu ia utilizá-los, eles estavam nulos… depois de um tempo quebrando a cabeça consegui achar o motivo!

Vamos exemplificar o cenário:

Entidade:

package com.mydomain.model.user;

public class User {

/** Número de identificação */
@Id
private Long id;

/** Nome de autenticação */
private String username;

//getters e setters
}

Metamodel:

package com.mydomain.metamodels;

import javax.persistence.metamodel.SingularAttribute;
import javax.persistence.metamodel.StaticMetamodel;

@StaticMetamodel(User.class)
public class User_ {
public static volatile SingularAttribute<User, String> username;
}

Uso do metamodel no predicado (predicate):


cb.equal(root.get(User_.username), “usuario_teste”);

Toda vez que eu tentava dar get(…) eu estava tomando nullpointerexception,
e a solução que encontrei foi colocar a classe User.class e a User_.class no mesmo pacote…
não sei o real motivo para precisarem estar, porém só assim funcionou pra mim..

Pesquisando descobri também que em futuros releases talvez essas classes possam ficar em pacotes diferentes,
segue algumas regras descritas na especificação do JPA 2:

  • Classes Metamodel devem estar no mesmo pacote das classes de entidade que elas descrevem;
  • Elas devem ter o mesmo nome das classes de entidade que elas descrevem, seguido por um traço-baixo (“_”, underline, underscore…).
    Exemplo: Produto.class é a classe de entidade e o Produto_.class é a classe metamodel.
  • Se uma entidade herdar de outra entidade ou de uma superclasse mapeada (mapped superclass) deve herdar da classe metamodel que descreve sua superclasse.
    Exemplo: Se ProdutoEspecial.class estende Product.class, que estende ObjetoPersistente.class, então ProdutoEspecial_.class deve estender Produto_.class, que deve estender ObjetoPersistente_.class.

É isso ai pessoal espero ter ajudado!
valeu!!!

Fonte: stackoverflow.com – debbie/Vítor E. Silva Souza

Anúncios

Como persistir LocalDate e LocalDateTime do Java 8 com JPA

E ai galera beleza? faz muito tempo que eu não postava nada novo, então bora com “novidade” do java 8 + JPA 😀

O Java 8 trouxe muitas grandes funcionalidades e uma das mais importantes e uma das antecipadas foi a nova API de data e hora. Haviam muitos problemas com a antiga API e não vou entrar em detalhes do porque nós precisamos de uma nova. Tenho certeza que você teve que lutar com ela com frequencia.

Todos esses problemas se foram com Java 8 \o/. A nova API de data e hora é bem desenhada, fácil de usar e (finalmente) imutável. O único problema que permanece é, que você não pode usava com JPA.

Bem, isso não totalmente verdade. Você pode usa-la, porém o JPA irá mapeá-la para BLOB ao invés de DATE ou TIMESTAMP. Que significa que a base de dados não está ciente do objeto de data e não pode aplicar nenhuma otimização para isso. E não é desse jeito que deveríamos ou gostaríamos de fazê-lo.

Porque o JPA não suporta LocalDate e LocalDateTime?

A resposta é simples, o JPA 2.1 foi liberado antes do Java 8 e a API de data e hora simplesmente não existia naquela época. Portanto a anotação @Temporal pode apenas ser aplicada a atributos do tipo java.util.Date e java.util.Calendar.

Se você deseja armazenar um atributo LocalDate em uma coluna Date ou uma LocalDateTime em uma coluna TIMESTAMP, você mesmo precisa definir o mapeamento para java.sql.Date ou java.sql.Timestamp. Graças ao conversor de atributo, uma das diversas novas funcionalidades do JPA 2.1, isso pode ser alcançado com apenas algumas linhas de código.

Nos exemplos abaixo, eu vou mostrar pra vocês como criar um conversor de atributos para LocalDate e LocalDateTime. Se você quer aprender mais sobre conversor de atributo, dê uma olhada aqui (inglês).

O exemplo

Antes de nós criar os conversores de atributo, vamos dar uma olhada na entidade de exemplo para esse post:

@Entity
public class MyEntity {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = “id”, updatable = false, nullable = false)
private Long id;

@Column
private LocalDate date;

@Column
private LocalDateTime dateTime;

}

O conversor de atributo faz parte da especificação do JPA 2.1 e pode portanto ser usado com qualquer implementação de JPA 2.1, ex.: Hibernate ou EclipseLink. Eu usei Wildfly 8.2 com Hibernate 4.3 para os seguintes exemplos.

Convertendo LocalDate

Como você pode ver no seguinte pedaço de código, não é necessário muita coisa para criar um conversor de atributo para LocalDate.

@Converter(autoApply = true)
public class LocalDateAttributeConverter implements AttributeConverter<LocalDate, Date> {

@Override
public Date convertToDatabaseColumn(LocalDate locDate) {
return (locDate == null ? null : Date.valueOf(locDate));
}

@Override
public LocalDate convertToEntityAttribute(Date sqlDate) {
return (sqlDate == null ? null : sqlDate.toLocalDate());
}
}

Você precisa implementar a interface AttributeConverter<LocalDate, Date> e seus 2 métodos convertToDatabaseColumn e convertToEntityAttribute. Como você pode ver nos nomes dos métodos, um deles define a conversão do tipo do atributo da entidade (LocalDate) para o tipo de coluna da base de dados (Date) e o outro é a conversão inversa. A conversão em si é simples porque java.sql.Date ja nos provê o método para fazer a conversão “de” e “para” um LocalDate.

Adicionalmente o conversor de atributo precisa ser anotado com a anotação @Converter. Devido a propriedade opcional autoApply=true, o conversor será aplicado a todos os atributos do tipo LocalDate. Dê uma olhada aqui (inglês), se você quer definir o uso de cada conversor para cada atributo individualmente.

A conversão do atributo é transparente para o desenvolvedor e o atributo LocalDate pode ser usado como qualquer outro atributo da entidade. Você pode usa-lo como parâmetro de query por exemplo.

LocalDate date = LocalDate.of(2015, 8, 11);
TypedQuery<MyEntity> query = this.em.createQuery(“SELECT e FROM MyEntity e WHERE date BETWEEN :start AND :end”, MyEntity.class);
query.setParameter(“start”, date.minusDays(2));
query.setParameter(“end”, date.plusDays(7));
MyEntity e = query.getSingleResult();

Convertendo LocalDateTime

O conversor de atributo para LocalDateTime é basicamente o mesmo. Você precisa implementar a interface attributeConverter<LocalDateTime, Timestamp> e o conversor precisa ser anotado com a anotação @Converter. Assim como o LocalDateConverter, a conversão entre LocalDateTime e um java.sql.Timestamp é feita através dos métodos de conversão do Timestamp.

@Converter(autoApply = true)
public class LocalDateTimeAttributeConverter implements AttributeConverter<LocalDateTime, Timestamp> {

@Override
public Timestamp convertToDatabaseColumn(LocalDateTime locDateTime) {
return (locDateTime == null ? null : Timestamp.valueOf(locDateTime));
}

@Override
public LocalDateTime convertToEntityAttribute(Timestamp sqlTimestamp) {
return (sqlTimestamp == null ? null : sqlTimestamp.toLocalDate());
}
}

Conclusão

O JPA 2.1 foi liberado antes do Java 8 e portanto não suporta a nova API de data e hora. Se você quer usar as novas classes (do jeito certo), você mesmo precisa definir a conversão para java,sql.Date e java.sql.Timestamp. Isso pode ser feito facilmente implementando a interface AttributeConverter<EntityType, DatabaseType> e anotando a classe com a anotação @Converter(autoApply=true). Setando autoApply=true, a conversão será aplicada a todos os atributos do EntityType e nenhuma alteração na entidade é necessária.

Até onde eu sei, a próxima versão do JPA suportará a nova API de data e hora e as diferentes implementações provavelmente irão suporta-la ainda mais cedo. O Hibernate 5 por exemplo irá suporta-la como uma funcionalidade proprietária.

É isso ai galera, espero tê-los ajudado 😀

Fonte: thoughts-on-java.org – Thorben Janssen

JPA2/Hibernate – Auto-incremento em colunas non-id (sem anotação @Id)

E ai galera beleza? hoje tive um problema ao tentar implementar:

@SequenceGenerator(name="MINHA_SEQUENCE", sequenceName="MINHA_SEQUENCE", allocationSize=1)
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="MINHA_SEQUENCE")

em uma coluna que não tinha @Id… simplesmente não estava criando a sequence, consequentemente, não gerava o valor automatico…

achei a seguinte solução: (não utiliza as anotações @SequenceGenerator e @GeneratedValue)

@Column(columnDefinition="serial")
@Generated(GenerationTime.INSERT)

testei no postgresql e funcionou perfeitamente!

Espero que ajude 😀 abraços !

Fonte: Sergey Vedernikov – Stackoverflow / axtavt – Stackoverflow

Spring Data JPA Avançado – Epecificações e queryDSL

Neste post eu gostaria de mergulhar dentro de alguns recursos e como eles poderiam lhe ajudar a simplificar a implementação da camada de acesso a dados ainda mais. A abstração do repositório Spring Data consiste de um modelo de programação baseado em interface, algumas classes de fábrica e um Spring namespace para facilmente configurar a infraestrutura. Uma interface de repositório típica se parece com isso:

public interface CustomerRepository extends JpaRepository<Customer, Long> {

Customer findByEmailAddress(String emailAddress);
List<Customer> findByLastname(String lastname, Sort sort);
Page<Customer> findByFirstname(String firstname, Pageable pageable);

}

O primeiro método simplesmente espera encontrar um único cliente com um determinado endereço de email, o segundo método retorna todos os clientes com um determinado sobrenome e aplica-se a ordenação determinada ao resultado, enquanto o terceiro método retorna uma página de clientes. Para mais  detalhes dê uma olhada neste artigo (inglês)

Embora essa abordagem seja realmente conveniente (você não precisa escrever uma única linha de implementação de código para ter as queries executadas) temos 2 desvantagens nisso: primeira, o número de métodos de consultas pode crescer para grandes aplicações por causa disso – e esse é o segundo ponto, as consultas definem um conjunto fixo de criterias. Para evitar essas 2 desvantagens, não seria legal se você pudesse chegar com um conjunto de atributos atômicos que você pudesse combinar dinamicamente para construir uma consulta?

Se você é uma usuário de JPA a muito tempo, você pode responder: não é pra isso que a API Criteria serve? Correto, então vamos dar uma olhada como se parece um exemplo de implementação de regra de negócio utilizando a API Criteria JPA. Esse é o caso de uso: nos aniversários deles nós queremos enviar um voucher para todos cliente de longa data. Como recuperaríamos aqueles com essa combinação?

Nós praticamente temos 2 partes para o predicate (atributo/predicado): o aniversário assim como o que nós chamamos de cliente-de-longa-data. Vamos assumir que isso significa que a criação da conta do cliente foi a pelo menos 2 anos atrás. Assim é como se pareceria a implementação utilizando a API Crieria JPA:

LocalDate today = new LocalDate();

CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Customer> query = builder.createQuery(Customer.class);
Root<Customer> root = query.from(Customer.class);

Predicate hasBirthday = builder.equal(root.get(Customer_.birthday), today);
Predicate isLongTermCustomer = builder.lessThan(root.get(Customer_.createdAt), today.minusYears(2);
query.where(builder.and(hasBirthday, isLongTermCustomer));
em.createQuery(query.select(root)).getResultList();

O que temos aqui? Criamos uma nova LocalDate por conveniência e seguimos com três linhas padrões para estabelecer as instâncias da infraestrutura JPA necessária. Então temos duas linhas construindo os atributos/predicados, uma para concatenar as duas e uma ultima para executar a consulta atual. Estamos usando classes de meta-modelagem introduzidas com JPA 2.0 e geradas pela API de Processamento de Anotações. O principal problema com este código é que os atributos não são fáceis de externalizar e reutilizar porque você precisa configurar o CriteriaBuilder, CriteriaQuery e o Root primeiro. Além da readaptação do código é tão pobre quanto é difícil de deduzir rapidamente a intenção do código à primeira vista.

Especificações
Para estar apto para definir atributos/predicados reutilizáveis nós introduzimos a interface de Especificação que é derivada dos conceitos introduzidos no livro Domain Driven Design de Eric Vans. Ele define uma especificação como um atributo sobre uma entidade a qual é exatamente o que nossa interface de especificação representa. Atualmente consiste em apenas um único método:

public interface Specification<T> {

Predicate toPredicate(Root<T> root, CriteriaQuery query, CriteriaBuilder cb);

}

então agora podemos facilmente utilizar uma classe utilitária como essa:

public CustomerSpecifications {
  public static Specification<Customer> customerHasBirthday() {
    return new Specification<Customer> {
      public Predicate toPredicate(Root<T> root, CriteriaQuery query, CriteriaBuilder cb) {
        return cb.equal(root.get(Customer_.birthday), today);
      }
    };
  }

  public static Specification<Customer> isLongTermCustomer() {
    return new Specification<Customer> {
      public Predicate toPredicate(Root<T> root, CriteriaQuery query, CriteriaBuilder cb) {
        return cb.lessThan(root.get(Customer_.createdAt), new LocalDate.minusYears(2));
      }
    };
  }
}

Reconhecidamente, não é o código mais bonito do mundo, mas fornece nossa necessidade inicial muito bem: podemos nos referir a um conjunto de especificações atômicas. A próxima questão é: como vamos executar essas especificações? Para fazer isso, você simplesmente estende JpaSpecificationExecutor na interface do seu repositório e assim “puxa” uma API para executar especificações:

public interface CustomerRepository extends JpaRepository<Customer>, JpaSpecificationExecutor {
  // Seus métodos de consulta aqui!!!
}

Um cliente pode agora fazer:

customerRepository.findAll(hasBirthday());
customerRepository.findAll(isLongTermCustomer());

A implementação do repositório básico irá preparar o CriteriaQuery, Root e CriteriaBuilder para você, aplique o atributo/predicado criado por uma especificação determinada e execute a consulta. Mas nós não poderiamos apenas ter criado métodos simples de consulta para realizar isso? Correto, mas lembre-se do nossa segunda necessidade inicial. Nós queremos estar aptos para combinar livremente especificações atômicas para criar novos na hora. Para fazer isso nós temos especificações da classe utilitária que fornece os métodos and(…) e or(…) para concatenar especificações anatomicas. Temos também um where(…) que fornece uma doce semântica que faz a expressão mais legível. O exemplo do caso se uso que vimos no começo parece com isso:

customerRepository.findAll(where(customerHasBirthday()).and(isLongTermCustomer()));

Isto fica mais legível, melhorando tanto a legibilidade quanto fornecendo flexibilidade adicional comparado ao uso da API Criteria JPA sozinha. A única ressalva aqui é que subindo com a implementação da especificação requer algum esforço  de codificação.

QueryDSL
Para curar essa dor um projeto open-source chamado Querydsl surgiu com uma abordagem bastante similar mas também diferente. Assim como a API Criteria JPA ela usa um processador de anotações Java 6 para gerar objetos meta-modelos mas produz uma API muito mais acessível. Outra coisa legal sobre o projeto, é que não tem suporte apenas para JPA, mas também aceita consultas Hibernate, JDO, Lucene, JDBC e até coleções simples.

Então para tê-lo instalado e rodando você adiciona o QueryDSL ao seu pom.xml e configura o plugin APT plugin conforme abaixo:

<plugin>
   <groupId>com.mysema.maven</groupId>
   <artifactId>maven-apt-plugin</artifactId>
   <version>1.0</version>
   <executions>
      <execution>
        <phase>generate-sources</phase>
        <goals>
          <goal>process</goal>
        </goals>
        <configuration>
            <outputDirectory>target/generated-sources</outputDirectory>
            <processor>com.mysema.query.apt.jpa.JPAAnnotationProcessor</processor>
        </configuration>
      </execution>
   </executions>
</plugin>

Isso irá fazer o seu build criar classes de consulta especiais – QCustomer dentro do mesmo pacote no nosso caso.

QCustomer customer = QCustomer.customer;
LocalDate today = new LocalDate();
BooleanExpression customerHasBirthday = customer.birthday.eq(today);
BooleanExpression isLongTermCustomer = customer.createdAt.lt(today.minusYears(2));

Este não é apenas o Inglês quase fluente fora da caixa (?), o BooleanExpressions, é mesmo reutilizável sem mais empacotamento do qual nos livra da especificação wrapper adicional (um pouco desagradável de implementar). Um plus adicional é que você tem o auto-complete de código da IDE a cada ponto do lado direito de atribuição, então customer. + CTRL + SPACE listará todas as propriedades. customer.birthday. + CTRL + SPACE listará todas as palavras-chave disponíveis e assim por diante. Para executar os atributos do QueryDSL você simplesmente estende QueryDslPredicateExecutor ao seu repositório:

public interface CustomerRepository extends JpaRepository<Customer>, QueryDslPredicateExecutor {
  // Seus métodos de consulta aqui!!!
}

Os clientes podem simplesmente fazer:

BooleanExpression customerHasBirthday = customer.birthday.eq(today);
BooleanExpression isLongTermCustomer = customer.createdAt.lt(today.minusYears(2));
customerRepository.findAll(customerHasBirthday.and(isLongTermCustomer));

Resumo
A abstração do repositório Spring Data JPA permite a execução de atributos/predicados ou via atributos API Criteria JPA wrapped dentro de um objeto de especificação ou via atributos QueryDSL. Para permitir essa funcionalidade você simplesmente deixa seu repositório extender JpaSpecificationExecutor ou QueryDslPredicateExecutor (você poderia até mesmo usar os 2 se você gostou). Veja que você precisa dos JAR’s do QueryDSL na classe no caso de você decidir pela abordagem do QueryDSL.

Mais uma coisa
Mais uma coisa legal sobre a abordagem do QueryDSL é que ela não está apenas disponível para os nossos repositórios JPA, mas para nosso apoio MongoDB também. A funcionalidade é inclusa apenas no release M2 do Spring Data MongoDB. Além de ambos módulos Mongo e JPA do Spring Data, são suportados na plataforma CloudFoundry. Veja os exemplos em cloudfoundry-samples wiki para iniciar  em Spring Data e CloudFoundry.

Fonte: SpringSource – Oliver Gierke

TransientObjectException, LazyInitializationException e outras famosas do Hibernate

Para quem desenvolve com Hibernate, sem dúvida as exceptions que aparecem mais são a TransientObjectException (TOE), LazyInitializationException (LIE) e a PersistentObjectException (POE). Semana passada tive o prazer de ministrar um treinamento de EJB3 e JSF para o pessoal da Petrobras de 5 cidades diferentes e durante o curso várias TOEs, POEs e LIEs apareceram. Vamos ver quando cada uma ocorre.

Estou usando aqui a API do Java Persistence, mas a relação é direta com o Hibernate, já que este é o meu provider. Considere duas entidades, Autor e Livro, cada uma com atributos triviais, um id que é @GeneratedValue e uma relação @ManyToMany entre elas, sendo Livro que possui o lado mappedBy. Vamos ao código:

Autor a = new Autor();
Livro l = new Livro();
a.setLivros(Collections.singleton(l));
manager.persist(a);

Resultado:

org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: br.com.caelum.hibernate.testes.Livro

Essa exception não ocorreria se você tivesse chamado manager.persist(l); dentro dessa mesma transação, ou utilizasse cascade=CascadeType.PERSIST no relacionamento. Um objeto transiente, ao ser persistido, não pode se referenciar a outros objetos transientes, a não ser que haja cascade!

Agora considere:

Autor a = new Autor();
a.setId(1L);
a.setNome("paulo");
    
manager.persist(a);

Resultado:

org.hibernate.PersistentObjectException: detached entity passed to persist: br.com.caelum.hibernate.testes.Autor

O id de Autor está anotado com @GeneratedValue, quando ele percebe que há um id setado ele imagina que provavelmente esse objeto já deve existir no banco de dados (repare que ele não faz select, se você colocar um id que não existe, a mesma exception ocorrerá). persist não aceita objetos detached, apenas transient e managed. Essa exception não ocorreria se seu id não fosse @GeneratedValue, ou se você tivesse puxado esse Autor através do manager.find, por exemplo.

Vamos passar para o método merge. Porque o utilizamos tanto? Pois na web, quando recebemos os parâmetros e populamos nosso Entity para atualizá-lo, ele está detached: não foi pego através do EntityManager/Session, porém possui um id setado (o que anteriormente gerou a PersistentObjectException). O merge tem um detalhe importantíssimo: ele não vai passar o estado daquela entidade para managed (como faz o saveOrUpdate do Hibernate), e sim devolver uma versão da mesma entidade que seja managed (como fazia o velho saveOrUpdateCopy), em outras palavras, futuras mudanças na entidade passada não surtirão efeito. A outra grande diferença é que agora podemos passar como argumento um objeto transiente que já possua um id setado:

Autor a = new Autor();
a.setId(1L);
a.setNome("livro1"); 
manager.merge(a);
a.setNome("livro2");

O código irá atualizar o nome do Livro de id 1 para livro1. Repare que a mudança para livro2 não surtirá efeito, já que o merge não faz attach do objeto passado como argumento, então nesse caso ele continua detached. Uma opção seria você pegar o retorno do método merge, que é o mesmo Autor, porém agora managed. A documentação do hibernate sobre o merge parece ser melhor que a especificação.

Aqui temos de ter muito cuidado, como não puxamos esse Autor pelo entityManager, caso ele possua alguns livros no seu relacionamento, nesse merge perderíamos todas essas informações!

Parece fácil evitar todas essas exceptions, então porque eu disse que ocorreram tanto no curso? Bem, estávamos usando a JPA de dentro de um container, não standalone. Considere então um session bean de granularidade fina que esteja agindo apenas como um dao (esse não é o ideal, mas fica para os testes):

@Remote
interface SessionBeanRemote {
  void persiste(Autor a);
  void persiste(Livro l);
  Livro buscaLivroPorNome(String nome);
}

E considere o cliente:

Livro l = new Livro();
sessionBeanRemoto.persiste(l);
Autor a = new Autor();
a.setLivros(Collections.singleton(l));
manager.persiste(a);

Repare como esse código é muito parecido com o primeiro desse post, porém já persistindo o Livro anteriormente, para evitar a TransientObjectException. Mas adivinhe, aqui é lançada uma TOE! Isso ocorre porque, apesar do Livro ter sido persistido, no cliente remoto ele estará transiente, pois sua chave primária não foi populada, já que o objeto foi serializado e só no servidor se encontra uma versão desse Livro com sua respectiva chave! Se fosse no EJB 2.x, onde o Entity bean também é um componente, isso não ocorreria. Mas aqui, fora do container, o Entity bean age realmente como um valeu object: não há ligação dele com o servidor.

Para resolver isso temos alguns idiomismos, mudaríamos nosso bean remoto para:

@Remote
interface SessionBeanRemote {
  Autor persiste(Autor a);
  Livro persiste(Livro l);
  Livro buscaLivroPorNome(String nome);
}

Um tanto estranho, e na nossa implementação devolveríamos o próprio argumento:

@Stateless
class SessionBean implements SessionBeanRemote {
  @PersistentContext
  private EntityManager manager;
  public Livro persiste(Livro l) {
    manager.persist(l);
    return l;
  }
  // outros metodos
}

Dessa maneira passaríamos de volta ao cliente uma versão detached do novo livro: agora com ID! Nosso cliente ficaria:

Livro l = new Livro();
l = sessionBeanRemoto.persiste(l); // agora pegamos o retorno!
Autor a = new Autor();
a.setLivros(Collections.singleton(l));
manager.persiste(a);

Existem outras alternativas, como retornar apenas a chave primária, utilizar session beans com maior granularidade, etc.

E a LazyInitializationException que mencionei? Usando hibernate ou JPA standalone estamos cansados de saber como evitá-la: basta manter a sessão aberta durante a renderização na camada de visualização. Mas com EJB a história é outra, sendo muito mais sutil: chame o método buscaPorNome, ele vai retornar ao cliente um Livro detached. Adivinhe o que acontece ao invocar o getAutores() no cliente?

Fonte: Paulo Silveira – Caelum