¿Cuál es la forma más fácil de ignorar un campo JPA durante la persistencia?

281

Básicamente estoy buscando una anotación de tipo "@Ignore" con la que pueda evitar que un campo en particular persista. ¿Cómo se puede lograr esto?

m2o
fuente

Respuestas:

450

@Transient Cumple con tus necesidades.

cletus
fuente
20
Pero entonces Jackson no serializará el campo cuando convierta a JSON ... ¿cómo resolverlo?
MobileMon
eso depende del diseño de su aplicación. si anota su clase de entidad, se aplica en todas partes; pero si anotateo que usa entidad - es otra historia. en resumen: use DAO cuando tenga múltiples almacenamientos
Andrii Plotnikov
No funciona en los campos de Lista. Para los campos de tipo Colección, hibernate en realidad crea una nueva tabla intermedia. ¿Alguna solución para eso?
zulkarnain shah
@MobileMon Puede resolverlo, consulte mi respuesta stackoverflow.com/a/41850392/3871754
Kamil Nekanowicz
@MobileMon Preferiblemente, no debería usar la misma entidad para fines de base de datos y API (responsabilidad única).
Jacek Ślimok
127

Para ignorar un campo, anótelo con @Transientpara que hibernate no lo asigne.

pero luego Jackson no serializará el campo cuando se convierta a JSON.

Si necesita mezclar JPA con JSON (omitido por JPA pero aún incluido en Jackson) use @JsonInclude:

@JsonInclude()
@Transient
private String token;

PROPINA:

También puede usar JsonInclude.Include.NON_NULL y ocultar campos en JSON durante la deserialización cuando token == null:

@JsonInclude(JsonInclude.Include.NON_NULL)
@Transient
private String token;
Kamil Nekanowicz
fuente
3
Estoy ejecutando JAX-RS 2.0.1 / Jersey 2.25.1 / Jackson 2.8.7 y con esa pila @JsonIncludeno es necesario: los @Transientcampos todavía están incluidos en JSON. (Todavía tienes mi voto: la técnica en sí misma podría ser muy útil en otras circunstancias).
DKroot
2
Lo hice en Spring Boot
Kamil Nekanowicz
hola, estoy tratando de usar esto, pero no se está serializando con el campo @Transient
Mohammad
@Mohammad agrega anotación @JsonInclude ()
Kamil Nekanowicz
Intenté esto en Spring Boot 2.1.7 pero no funcionó. Pero aún así obtienes mi voto porque puede funcionar en otros escenarios.
Aditya Ekbote
19

Esta respuesta llega un poco tarde, pero completa la respuesta.

Para evitar que un campo de una entidad persista en DB, se puede usar uno de los dos mecanismos:

@Transient : la anotación JPA que marca un campo como no persistente

palabra clave transitoria en java. Cuidado: el uso de esta palabra clave evitará que el campo se use con cualquier mecanismo de serialización de java. Entonces, si el campo debe ser serializado, será mejor que use solo laanotación @Transient .

Olimpiu POP
fuente
1
¿Qué tal si solo quiero ignorar la persistencia en el método get ?. Por ejemplo: myjparepository.save () guardará el modelo como de costumbre, y myjparepository.find (id) ignorará el campo que quiero.
xtiger 01 de
@xtiger puede crear una consulta personalizada para archivar esa funcionalidad. Aquí hay un enlace de
Mohale
7

Existen múltiples soluciones según el tipo de atributo de la entidad.

Atributos básicos

Considere que tiene la siguiente accounttabla:

La tabla de cuentas

La accounttabla se asigna a la Accountentidad de esta manera:

@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
}

Los atributos básicos entidad se asignan a las columnas de las tablas, así como propiedades id, iban, centsson atributos básicos.

Pero el dollars, interestCentsy interestDollarsson propiedades computarizada, para que ellos con anotaciones @Transientpara excluirlos de SELECT, INSERT, UPDATE y DELETE sentencias SQL.

Por lo tanto, para los atributos básicos, debe usar @Transientpara excluir una propiedad dada de ser persistente.

Para obtener más detalles sobre los atributos de entidad calculados, consulte este artículo .

Asociaciones

Suponiendo que tiene lo siguiente posty las post_commenttablas:

Las tablas post y post_comment

Desea asignar la lastestCommentasociación en la Postentidad a la última PostCommententidad que se agregó.

Para hacer eso, puedes usar la @JoinFormulaanotación:

@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
}

Al recuperar la Postentidad, puede ver que latestCommentse recupera, pero si desea modificarla, el cambio se ignorará.

Por lo tanto, para las asociaciones, puede usar @JoinFormulapara ignorar las operaciones de escritura mientras todavía permite leer la asociación.

Para obtener más detalles sobre las asociaciones calculadas, consulte este artículo .

@MapsId

Otra forma de ignorar las asociaciones que ya están asignadas por el identificador de entidad es usar @MapsId.

Por ejemplo, considere la siguiente relación de tabla uno a uno:

Las tablas post y post_details

La PostDetailsentidad se mapea así:

@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 tanto el idatributo como la postasociación asignan la misma columna de la base de datos, que es la post_detailscolumna Clave primaria.

Para excluir el idatributo, la @MapsIdanotación le indicará a Hibernate que la postasociación se encarga del valor de la columna Clave primaria de la tabla.

Entonces, cuando el identificador de entidad y una asociación comparten la misma columna, puede usar @MapsIdpara ignorar el atributo de identificador de entidad y usar la asociación en su lugar.

Para obtener más detalles al respecto @MapsId, consulte este artículo .

Utilizando insertable = false, updatable = false

Otra opción es utilizar insertable = false, updatable = falsepara la asociación que Hibernate desea ignorar.

Por ejemplo, podemos mapear la asociación individual anterior de esta manera:

@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();
        }
    }
}

Los atributos insertabley updatablede la @JoinColumnanotación le indicarán a Hibernate que ignore la postasociación, ya que el identificador de la entidad se ocupa de la post_idcolumna Clave primaria.

Vlad Mihalcea
fuente
Tenga en cuenta que tanto el atributo id como la asociación posterior asignan la misma columna de la base de datos, que es la columna Clave primaria post_comment. ¿Es esto correcto en el contexto de posty post_detailstablas como ejemplo?
David
1
Gracias por verlo. Lo arreglé.
Vlad Mihalcea
3

Para completar las respuestas anteriores, tuve el caso usando un archivo de mapeo XML donde ni el @Transientni transientfuncionó ... Tuve que poner la información transitoria en el archivo xml:

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

Ninguna de las respuestas anteriores funcionó para mí usando Hibernate 5.2.10, Jersey 2.25.1 y Jackson 2.8.9. Finalmente encontré la respuesta (más o menos, hacen referencia a hibernate4module pero también funciona para 5) aquí . Ninguna de las anotaciones de Json funcionó en absoluto @Transient. Al parecer, Jackson2 es lo suficientemente "inteligente" como para ignorar amablemente las cosas marcadas con, a @Transientmenos que explícitamente le digas que no lo haga. La clave era agregar el módulo hibernate5 (que estaba usando para lidiar con otras anotaciones de Hibernate) y desactivar la USE_TRANSIENT_ANNOTATIONfunción en mi aplicación Jersey:

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

Aquí está la dependencia para el Hibernate5Module:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-hibernate5</artifactId>
    <version>2.8.9</version>
</dependency>
Hodglem
fuente
Después de probar cualquier otro método mencionado anteriormente y otros, @JsonProperty @JsonInclude @JsonSerialize + @JsonDeserialize mapper.configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, false));esta solución finalmente funcionó. ¡Gracias!
Aceonline
1

A veces quieres:

  1. Serializar una columna
  2. Ignorar la persistencia de la columna:

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

Un buen escenario es cuando una determinada columna se calcula automáticamente utilizando otros valores de columna

Obothlale
fuente