É possível usar uma sequência de banco de dados para alguma coluna que não é o identificador / não faz parte de um identificador composto ?
Estou usando o hibernate como provedor jpa e tenho uma tabela que possui algumas colunas que são geradas por valores (usando uma sequência), embora elas não façam parte do identificador.
O que eu quero é usar uma sequência para criar um novo valor para uma entidade, onde a coluna para a sequência NÃO é (parte da) a chave primária:
@Entity
@Table(name = "MyTable")
public class MyEntity {
//...
@Id //... etc
public Long getId() {
return id;
}
//note NO @Id here! but this doesn't work...
@GeneratedValue(strategy = GenerationType.AUTO, generator = "myGen")
@SequenceGenerator(name = "myGen", sequenceName = "MY_SEQUENCE")
@Column(name = "SEQ_VAL", unique = false, nullable = false, insertable = true, updatable = true)
public Long getMySequencedValue(){
return myVal;
}
}
Então, quando eu faço isso:
em.persist(new MyEntity());
o ID será gerado, mas a mySequenceVal
propriedade também será gerada pelo meu provedor de JPA.
Apenas para esclarecer: quero que o Hibernate gere o valor da mySequencedValue
propriedade. Sei que o Hibernate pode lidar com valores gerados por banco de dados, mas não quero usar um gatilho ou qualquer outra coisa além do próprio Hibernate para gerar o valor para minha propriedade. Se o Hibernate pode gerar valores para chaves primárias, por que não pode gerar para uma propriedade simples?
@GeneratedValue
campos que não são id. Por favor vote para ser incluído em 2,2 java.net/jira/browse/JPA_SPEC-113Achei que
@Column(columnDefinition="serial")
funciona perfeito, mas apenas para o PostgreSQL. Para mim, essa foi a solução perfeita, porque a segunda entidade é a opção "feia".fonte
columnDefinition=
bit basicamente diz ao Hiberate para não tentar gerar a definição da coluna e usar o texto que você forneceu. Essencialmente, seu DDL para a coluna será literalmente apenas nome + definição de coluna. Nesse caso (PostgreSQL),mycolumn serial
é uma coluna válida em uma tabela.@Column(columnDefinition = "integer auto_increment")
@Column(insertable = false, updatable = false, columnDefinition="serial")
impedir que o hibernate tentasse inserir valores nulos ou atualizar o campo. Você precisará consultar novamente o banco de dados para obter o ID gerado após uma inserção, se precisar usá-lo imediatamente.Eu sei que essa é uma pergunta muito antiga, mas é mostrada primeiramente nos resultados e o jpa mudou muito desde a pergunta.
A maneira correta de fazer isso agora é com a
@Generated
anotação. Você pode definir a sequência, definir o padrão na coluna para essa sequência e mapear a coluna como:fonte
O Hibernate definitivamente suporta isso. Dos documentos:
"Propriedades geradas são propriedades cujos valores são gerados pelo banco de dados. Normalmente, os aplicativos Hibernate são necessários para atualizar objetos que contêm quaisquer propriedades para as quais o banco de dados estava gerando valores. No entanto, marcar propriedades como geradas permite que o aplicativo delegue essa responsabilidade ao Hibernate. Essencialmente, sempre que o Hibernate emite um SQL INSERT ou UPDATE para uma entidade que definiu propriedades geradas, ele emite imediatamente uma seleção posteriormente para recuperar os valores gerados. "
Para propriedades geradas apenas na inserção, seu mapeamento de propriedade (.hbm.xml) seria semelhante a:
Para propriedades geradas ao inserir e atualizar seu mapeamento de propriedades (.hbm.xml) seria semelhante a:
Infelizmente, eu não conheço JPA, então não sei se esse recurso é exposto via JPA (suspeito que possivelmente não)
Como alternativa, você deve poder excluir a propriedade de inserções e atualizações e depois "manualmente" chamar session.refresh (obj); depois de inseri-lo / atualizá-lo para carregar o valor gerado do banco de dados.
É assim que você excluiria a propriedade do uso nas instruções de inserção e atualização:
Novamente, não sei se o JPA expõe esses recursos do Hibernate, mas o Hibernate os suporta.
fonte
Como acompanhamento, aqui está como eu consegui que funcionasse:
fonte
SQLQuery sqlQuery = getSession().createSQLQuery("select NAMED_SEQ.nextval seq from dual"); sqlQuery.addScalar("seq", LongType.INSTANCE); return (Long) sqlQuery.uniqueResult();
Corrigi a geração de UUID (ou sequências) com o Hibernate usando a
@PrePersist
anotação:fonte
Embora esse seja um tópico antigo, quero compartilhar minha solução e espero obter algum feedback sobre isso. Esteja avisado de que eu só testei esta solução com meu banco de dados local em alguns casos de teste JUnit. Portanto, este não é um recurso produtivo até agora.
Resolvi esse problema introduzindo uma anotação personalizada chamada Sequence sem propriedade. É apenas um marcador para os campos aos quais deve ser atribuído um valor a partir de uma sequência incrementada.
Usando esta anotação, marquei minhas entidades.
Para manter as coisas independentes do banco de dados, introduzi uma entidade chamada SequenceNumber que contém o valor atual da sequência e o tamanho do incremento. Eu escolhi o className como chave exclusiva para que cada classe de entidade obtenha sua própria sequência.
A última etapa e a mais difícil é um PreInsertListener que lida com a atribuição do número de sequência. Note que eu usei a primavera como recipiente de feijão.
Como você pode ver no código acima, o ouvinte usou uma instância SequenceNumber por classe de entidade e reserva alguns números de sequência definidos pelo incrementValue da entidade SequenceNumber. Se ficar sem números de sequência, ele carregará a entidade SequenceNumber para a classe de destino e reserva os valores incrementValue para as próximas chamadas. Dessa forma, não preciso consultar o banco de dados sempre que um valor de sequência for necessário. Observe a StatelessSession que está sendo aberta para reservar o próximo conjunto de números de sequência. Você não pode usar a mesma sessão em que a entidade de destino atualmente persiste, pois isso levaria a uma ConcurrentModificationException no EntityPersister.
Espero que isso ajude alguém.
fonte
Se você estiver usando o postgresql
e eu estiver usando no boot da primavera 1.5.6
fonte
seq_order
e fazer referência ao camponextval('seq_order'::regclass)
Eu corro na mesma situação que você e também não encontrei respostas sérias se for basicamente possível gerar propriedades sem identificação com JPA ou não.
Minha solução é chamar a sequência com uma consulta JPA nativa para definir a propriedade manualmente antes de persistir.
Isso não é satisfatório, mas funciona como uma solução alternativa no momento.
Mario
fonte
Encontrei esta observação específica na sessão 9.1.9 Anotação GeneratedValue da especificação JPA: "[43] Aplicativos portáteis não devem usar a anotação GeneratedValue em outros campos ou propriedades persistentes". Portanto, presumo que não seja possível gerar automaticamente valor para valores de chave não primária pelo menos usando simplesmente o JPA.
fonte
Parece que o thread é antigo, eu só queria adicionar minha solução aqui (usando o AspectJ-AOP na primavera).
A solução é criar uma anotação personalizada da
@InjectSequenceValue
seguinte maneira.Agora você pode anotar qualquer campo na entidade, para que o valor do campo subjacente (Longo / Inteiro) seja injetado no tempo de execução usando o próximo valor da sequência.
Anote assim.
Até agora, marcamos o campo que precisamos para injetar o valor da sequência. Então, veremos como injetar o valor da sequência nos campos marcados, isso é feito criando o corte de ponto no AspectJ.
Iremos disparar a injeção imediatamente antes da
save/persist
execução do método. Isso é feito na classe abaixo.Agora você pode anotar qualquer Entidade como abaixo.
fonte
"Não quero usar um gatilho ou qualquer outra coisa além do próprio Hibernate para gerar o valor para minha propriedade"
Nesse caso, que tal criar uma implementação de UserType que gere o valor necessário e configurar os metadados para usar esse UserType para persistência da propriedade mySequenceVal?
fonte
Isso não é o mesmo que usar uma sequência. Ao usar uma sequência, você não está inserindo ou atualizando nada. Você está simplesmente recuperando o próximo valor da sequência. Parece que o hibernate não suporta.
fonte
Se você tiver uma coluna com o tipo UNIQUEIDENTIFIER e a geração padrão necessária na inserção, mas a coluna não for PK
Em db você terá
Nesse caso, você não definirá o gerador para um valor que você precisa (será automaticamente graças a
columnDefinition="UNIQUEIDENTIFIER"
). O mesmo que você pode tentar para outros tipos de colunafonte
Eu encontrei uma solução alternativa para isso nos bancos de dados MySql usando @PostConstruct e JdbcTemplate em um aplicativo Spring. Pode ser possível com outros bancos de dados, mas o caso de uso que apresentarei é baseado na minha experiência com o MySql, pois ele usa auto_increment.
Primeiro, tentei definir uma coluna como auto_increment usando a propriedade ColumnDefinition da anotação @Column, mas ela não estava funcionando, pois a coluna precisava ser uma chave para ser incrementada automaticamente, mas aparentemente a coluna não seria definida como um índice até depois de ser definido, causando um impasse.
Aqui é onde eu vim com a idéia de criar a coluna sem a definição de auto_increment e adicioná-la depois a criação do banco de dados. Isso é possível usando a anotação @PostConstruct, que faz com que um método seja chamado logo após o aplicativo inicializar os beans, juntamente com o método de atualização do JdbcTemplate.
O código é o seguinte:
Em Minha Entidade:
Em uma classe PostConstructComponent:
fonte
Quero fornecer uma alternativa ao lado da solução aceita do @Morten Berg, que funcionou melhor para mim.
Essa abordagem permite definir o campo com o
Number
tipo realmente desejado -Long
no meu caso de uso - em vez deGeneralSequenceNumber
. Isso pode ser útil, por exemplo, para serialização (des) JSON.A desvantagem é que requer um pouco mais de sobrecarga do banco de dados.
Primeiro, precisamos de um
ActualEntity
no qual queremos incrementar automaticamente ogenerated
tipoLong
:Em seguida, precisamos de uma entidade auxiliar
Generated
. Coloquei o pacote private ao lado deActualEntity
, para manter um detalhe de implementação do pacote:Finalmente, precisamos de um lugar para conectar antes de salvar o arquivo
ActualEntity
. Lá, criamos e persistimos umaGenerated
instância. Isso fornece uma sequência de banco de dados geradaid
do tipoLong
. Utilizamos esse valor escrevendo-o paraActualEntity.generated
.No meu caso de uso, implementei isso usando um Spring Data REST
@RepositoryEventHandler
, que é chamado logo antesActualEntity
da persistência. Deve demonstrar o princípio:Não testei em um aplicativo da vida real; portanto, aproveite com cuidado.
fonte
Eu estive em uma situação como você (sequência JPA / Hibernate para campo não @Id) e acabei criando um gatilho no meu esquema db que adiciona um número de sequência exclusivo na inserção. Eu nunca consegui trabalhar com o JPA / Hibernate
fonte
Depois de passar horas, isso me ajudou a resolver meu problema:
Para Oracle 12c:
Para H2:
Faça também:
fonte