Qual é a maneira mais fácil de ignorar um campo JPA durante a persistência?

281

Estou basicamente procurando por uma anotação do tipo "@Ignore" com a qual eu possa impedir que um determinado campo seja persistido. Como isso pode ser alcançado?

m2o
fonte

Respostas:

450

@Transient está em conformidade com suas necessidades.

cleto
fonte
20
Mas então jackson não serializará o campo ao converter para JSON ... como resolver?
precisa saber é o seguinte
isso depende da criação do seu aplicativo. se você anotar sua classe de entidade - ela se aplica a todos os lugares; mas se você anotar dao que usa entidade - é outra história. em suma: o uso DAO quando você tem vários armazenamentos
Andrii Plotnikov
Não funciona nos campos da lista. Para os campos do tipo Coleção, o hibernate realmente cria uma nova tabela intermediária. Alguma solução alternativa para isso?
Zulkarnain shah
@MobileMon Você pode resolvê-lo, verifique a minha resposta stackoverflow.com/a/41850392/3871754
Kamil Nekanowicz
@MobileMon De preferência, você não deve usar a mesma entidade para fins de banco de dados e API (responsabilidade única).
Jacek Ślimok
126

Para ignorar um campo, anote-o com, @Transientpara que não seja mapeado pela hibernação.

mas jackson não serializará o campo ao converter para JSON.

Se você precisar misturar JPA com JSON (omitir JPA, mas ainda incluir em Jackson), use @JsonInclude:

@JsonInclude()
@Transient
private String token;

DICA:

Você também pode usar JsonInclude.Include.NON_NULL e ocultar campos no JSON durante a desserialização quando token == null:

@JsonInclude(JsonInclude.Include.NON_NULL)
@Transient
private String token;
Kamil Nekanowicz
fonte
3
Estou executando o JAX-RS 2.0.1 / Jersey 2.25.1 / Jackson 2.8.7 e com essa pilha @JsonIncludenão é necessário: os @Transientcampos ainda estão incluídos no JSON. (Você ainda recebeu meu voto: a técnica em si pode ser muito útil em outras circunstâncias).
DKroot
2
Eu fiz isso no Spring Boot #
Kamil Nekanowicz
oi, eu estou tentando usar isso, mas não está sendo serializado com campo @Transient
Mohammad
@Mohammad adicionar anotação @JsonInclude ()
Kamil Nekanowicz
Tentei isso no Spring Boot 2.1.7, mas não funcionou. Mas você ainda recebe meu voto, pois pode funcionar em outros cenários.
Aditya Ekbote 28/08/19
19

Essa resposta chega um pouco tarde, mas completa a resposta.

Para evitar que um campo de uma entidade seja persistido no DB, pode-se usar um dos dois mecanismos:

@Transient - a anotação JPA que marca um campo como não persistente

palavra-chave transitória em java. Cuidado - usar esta palavra-chave impedirá que o campo seja usado com qualquer mecanismo de serialização do java. Portanto, se o campo precisar ser serializado, é melhor usar apenas aanotação @Transient .

Olimpiu POP
fonte
1
que tal eu só quero ignorar a persistência no método get? Por exemplo: myjparepository.save () salvará o modelo como de costume, e myjparepository.find (id) ignorará o campo que eu quero?
Xtiger
@xtiger, você pode criar uma consulta personalizada para arquivar essa funcionalidade. Aqui está um exemplo de link
Mohale
7

Existem várias soluções, dependendo do tipo de atributo da entidade.

Atributos básicos

Considere que você tem a seguinte accounttabela:

A tabela da conta

A accounttabela é mapeada para a Accountentidade assim:

@Entity(name = "Account")
public class Account {

    @Id
    private Long id;

    @ManyToOne
    private User owner;

    private String iban;

    private long cents;

    private double interestRate;

    private Timestamp createdOn;

    @Transient
    private double dollars;

    @Transient
    private long interestCents;

    @Transient
    private double interestDollars;

    @PostLoad
    private void postLoad() {
        this.dollars = cents / 100D;

        long months = createdOn.toLocalDateTime()
            .until(LocalDateTime.now(), ChronoUnit.MONTHS);
        double interestUnrounded = ( ( interestRate / 100D ) * cents * months ) / 12;
        this.interestCents = BigDecimal.valueOf(interestUnrounded)
            .setScale(0, BigDecimal.ROUND_HALF_EVEN).longValue();

        this.interestDollars = interestCents / 100D;
    }

    //Getters and setters omitted for brevity
}

As propriedades básicas da entidade são mapeados para as colunas da tabela, assim como propriedades id, iban, centssão atributos básicos.

Mas a dollars, interestCentse interestDollarssão propriedades calculado, para que anotá-los com @Transienta excluí-los de SELECT, INSERT, UPDATE e instruções SQL DELETE.

Portanto, para atributos básicos, você precisa usar @Transientpara excluir uma determinada propriedade da persistência.

Para mais detalhes sobre atributos de entidade calculados, consulte este artigo .

Associações

Supondo que você tenha o seguinte poste post_commenttabelas:

As tabelas post e post_comment

Você deseja mapear a lastestCommentassociação na Postentidade para a PostCommententidade mais recente que foi adicionada.

Para fazer isso, você pode usar a @JoinFormulaanotação:

@Entity(name = "Post")
@Table(name = "post")
public class Post {

    @Id
    private Long id;

    private String title;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinFormula("(" +
        "SELECT pc.id " +
        "FROM post_comment pc " +
        "WHERE pc.post_id = id " +
        "ORDER BY pc.created_on DESC " +
        "LIMIT 1" +
    ")")
    private PostComment latestComment;

    //Getters and setters omitted for brevity
}

Ao buscar a Postentidade, você pode ver que ela latestCommentfoi buscada, mas se você quiser modificá-la, a alteração será ignorada.

Portanto, para associações, você pode usar @JoinFormulapara ignorar as operações de gravação enquanto ainda permite a leitura da associação.

Para mais detalhes sobre associações computadas, consulte este artigo .

@MapsId

Outra maneira de ignorar associações que já estão mapeadas pelo identificador de entidade é usar @MapsId.

Por exemplo, considere o seguinte relacionamento de tabela um para um:

As tabelas post e post_details

A PostDetailsentidade é mapeada assim:

@Entity(name = "PostDetails")
@Table(name = "post_details")
public class PostDetails {

    @Id
    private Long id;

    @Column(name = "created_on")
    private Date createdOn;

    @Column(name = "created_by")
    private String createdBy;

    @OneToOne(fetch = FetchType.LAZY)
    @MapsId
    private Post post;

    public PostDetails() {}

    public PostDetails(String createdBy) {
        createdOn = new Date();
        this.createdBy = createdBy;
    }

    //Getters and setters omitted for brevity
}

Observe que o idatributo e a postassociação mapeiam a mesma coluna do banco de dados, que é a post_detailscoluna Chave Primária.

Para excluir o idatributo, a @MapsIdanotação informará ao Hibernate que a postassociação cuida do valor da coluna Chave Primária da tabela.

Portanto, quando o identificador de entidade e uma associação compartilham a mesma coluna, você pode usar @MapsIdpara ignorar o atributo identificador de entidade e usar a associação.

Para mais detalhes @MapsId, consulte este artigo .

Usando insertable = false, updatable = false

Outra opção é usar insertable = false, updatable = falsepara a associação que você deseja que o Hibernate ignore.

Por exemplo, podemos mapear a associação individual anterior como esta:

@Entity(name = "PostDetails")
@Table(name = "post_details")
public class PostDetails {

    @Id
    @Column(name = "post_id")
    private Long id;

    @Column(name = "created_on")
    private Date createdOn;

    @Column(name = "created_by")
    private String createdBy;

    @OneToOne
    @JoinColumn(name = "post_id", insertable = false, updatable = false)
    private Post post;

    //Getters and setters omitted for brevity

    public void setPost(Post post) {
        this.post = post;
        if (post != null) {
            this.id = post.getId();
        }
    }
}

Os atributos insertablee updatableda @JoinColumnanotação instruirão o Hibernate a ignorar a postassociação, pois o identificador da entidade cuida da post_idcoluna Chave Primária.

Vlad Mihalcea
fonte
Observe que o atributo id e a associação posterior mapeiam a mesma coluna do banco de dados, que é a coluna Chave Primária post_comment. Isso está correto no contexto de poste post_detailstabelas como exemplo?
David
1
Obrigado por detectá-lo. Eu consertei isso.
Vlad Mihalcea 20/03
3

Para concluir as respostas acima, tive o caso usando um arquivo de mapeamento XML em que nem o @Transientnem transientfuncionava ... tive que colocar as informações transitórias no arquivo xml:

<attributes>
(...)
    <transient name="field" />
</attributes>
Vinze
fonte
2

Nenhuma das respostas acima funcionou para mim usando o Hibernate 5.2.10, Jersey 2.25.1 e Jackson 2.8.9. Finalmente encontrei a resposta (mais ou menos, eles fazem referência ao hibernate4module, mas funciona para 5 também) aqui . Nenhuma das anotações de Json funcionou @Transient. Aparentemente, o Jackson2 é 'inteligente' o suficiente para ignorar gentilmente as coisas marcadas com, a @Transientmenos que você diga explicitamente que não. A chave era adicionar o módulo hibernate5 (que eu estava usando para lidar com outras anotações do Hibernate) e desativar o USE_TRANSIENT_ANNOTATIONrecurso no meu aplicativo Jersey:

ObjectMapper jacksonObjectMapper = new ObjectMapper();
Hibernate5Module jacksonHibernateModule = new Hibernate5Module();
jacksonHibernateModule.disable(Hibernate5Module.Feature.USE_TRANSIENT_ANNOTATION);
jacksonObjectMapper.registerModule(jacksonHibernateModule);  

Aqui está a dependência do Hibernate5Module:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-hibernate5</artifactId>
    <version>2.8.9</version>
</dependency>
Hodglem
fonte
Depois de tentar todos os outros métodos mencionados acima e outros, @JsonProperty @JsonInclude @JsonSerialize + @JsonDeserialize mapper.configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, false));essa solução finalmente funcionou. Obrigado!
Aceonline
1

Às vezes você deseja:

  1. Serializar uma coluna
  2. Ignore a coluna de persistir:

Usar @Column(name = "columnName", insertable = false, updatable = false)

Um bom cenário é quando uma determinada coluna é calculada automaticamente usando outros valores de coluna

Obothlale
fonte