Какой самый простой способ игнорировать поле JPA во время сохранения?


281

По сути, я ищу аннотацию типа «@Ignore», с помощью которой я могу остановить сохранение определенного поля. Как этого достичь?

Ответы:


450

@Transient соответствует вашим потребностям.


20
Но тогда Джексон не будет сериализовать поле при конвертации в JSON ... как решить?
MobileMon

это зависит от вашего приложения. если вы аннотируете свой класс сущностей - он применяется везде; но если вы аннотируете Дао, который использует сущность - это другая история. короче говоря: используйте DAO, когда у вас есть несколько хранилищ
Андрей Плотников

Не работает с полями списка. Для полей типа Коллекция Hibernate фактически создает новую промежуточную таблицу. Любой обходной путь для этого?
Зулькарнайн шах

@MobileMon Вы можете решить эту проблему, проверьте мой ответ stackoverflow.com/a/41850392/3871754
Камил Неканович

@MobileMon Желательно, чтобы вы не использовали одну и ту же сущность как для базы данных, так и для API (единоличная ответственность).
Яцек Элимок

127

Чтобы игнорировать поле, пометьте его, @Transientчтобы оно не отображалось в спящем режиме.

но тогда Джексон не будет сериализовать поле при преобразовании в JSON.

Если вам нужно смешать JPA с JSON (опускается JPA, но все еще включается в Джексона), используйте @JsonInclude:

@JsonInclude()
@Transient
private String token;

НАКОНЕЧНИК:

Вы также можете использовать JsonInclude.Include.NON_NULL и скрывать поля в JSON во время десериализации, когда token == null:

@JsonInclude(JsonInclude.Include.NON_NULL)
@Transient
private String token;

3
Я использую JAX-RS 2.0.1 / Джерси 2.25.1 / Джексон 2.8.7, и с этим стеком @JsonIncludeнет необходимости: @Transientполя все еще включены в JSON. (Вы все еще получили мой голос: сама техника может быть очень полезна в других обстоятельствах).
DKroot

2
Я сделал это на Spring Boot
Камиль Неканович

Привет, я пытаюсь использовать это, но это не сериализовано с полем @Transient
Мохаммед

@ Мохаммед добавить аннотацию @JsonInclude ()
Камил Неканович

Пробовал это в Spring Boot 2.1.7, но не сработало. Но все же вы получаете мой голос, потому что он может работать в других сценариях.
Адитья Экбот


19

Этот ответ приходит немного поздно, но он завершает ответ.

Во избежание сохранения поля из сущности в БД можно использовать один из двух механизмов:

@Transient - аннотация JPA, помечающая поле как нестабильное

временное ключевое слово в Java. Осторожно - использование этого ключевого слова предотвратит использование поля с любым механизмом сериализации из Java. Итак, если поле должно быть сериализовано, вам лучше использоватьаннотацию @Transient .


1
Как насчет того, чтобы просто игнорировать постоянство метода get? Например: myjparepository.save () сохранит модель как обычно, а myjparepository.find (id) проигнорирует поле, которое мне нужно?
xtiger

@xtiger вы можете создать собственный запрос для архивации этой функциональности. Вот пример ссылки
Мохале

7

Существует несколько решений в зависимости от типа атрибута объекта.

Основные атрибуты

Предположим, у вас есть следующая accountтаблица:

Таблица счетов

accountТаблица отображается в Accountсущности , как это:

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

Основные атрибуты сущностей отображаются в столбцах таблицы, поэтому свойства , такие как id, iban, centsявляются основными атрибутами.

Но dollars, interestCentsи interestDollarsвычисляются свойства, так что вы аннотировать их , @Transientчтобы исключить их из SELECT, INSERT, UPDATE и DELETE заявления SQL.

Итак, для базовых атрибутов вам нужно использовать @Transient, чтобы исключить сохранение данного свойства.

Для получения дополнительной информации о вычисляемых атрибутах сущности, ознакомьтесь с этой статьей .

ассоциации

Предполагая, что у вас есть следующее postи post_commentтаблицы:

Таблицы post и post_comment

Вы хотите сопоставить lastestCommentассоциацию в Postсущности с последней PostCommentдобавленной сущностью.

Для этого вы можете использовать @JoinFormulaаннотацию:

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

При извлечении Postсущности вы можете видеть, что latestCommentона извлекается, но если вы хотите изменить ее, изменение будет проигнорировано.

Таким образом, для ассоциаций вы можете использовать, @JoinFormulaчтобы игнорировать операции записи, но при этом разрешать чтение ассоциации.

Для более подробной информации о вычисляемых ассоциациях, посмотрите эту статью .

@MapsId

Другой способ игнорировать ассоциации, которые уже сопоставлены идентификатором объекта, - использовать @MapsId.

Например, рассмотрим следующую взаимосвязь таблицы «один к одному»:

Таблицы post и post_details

PostDetailsОбъект отображается следующим образом:

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

Обратите внимание, что и idатрибут, и postсопоставление отображают один и тот же столбец базы данных, который является post_detailsстолбцом первичного ключа.

Чтобы исключить idатрибут, @MapsIdаннотация сообщит Hibernate, что postассоциация заботится о значении столбца первичного ключа таблицы.

Таким образом, когда идентификатор объекта и ассоциация совместно используют один и тот же столбец, вы можете использовать, @MapsIdчтобы игнорировать атрибут идентификатора объекта и использовать вместо него ассоциацию.

Для более подробной информации @MapsId, ознакомьтесь с этой статьей .

С помощью insertable = false, updatable = false

Другой вариант - использовать insertable = false, updatable = falseдля ассоциации, которую вы хотите игнорировать в Hibernate.

Например, мы можем сопоставить предыдущую однозначную ассоциацию следующим образом:

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

В insertableи updatableатрибутах @JoinColumnаннотации будут инструктировать Hibernate игнорировать postассоциацию с момента субъекта идентификатора заботится о post_idстолбце первичного ключа.


Обратите внимание, что атрибут id и пост-ассоциация сопоставляют один и тот же столбец базы данных, который является столбцом первичного ключа post_comment. Это правильно в контексте postи post_detailsтаблиц в качестве примера?
Дэвид

1
Спасибо, что заметили это. Я починил это.
Влад Михальча

3

Чтобы завершить приведенные выше ответы, у меня был случай, использующий файл сопоставления XML, где ни сработало, @Transientни transientсработало ... Мне пришлось поместить временную информацию в файл XML:

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

2

Ни один из приведенных выше ответов не работал для меня, используя Hibernate 5.2.10, Jersey 2.25.1 и Jackson 2.8.9. Я наконец нашел ответ (вроде как они ссылаются на hibernate4module, но он работает и для 5) здесь . Ни одна из аннотаций Json не работала вообще @Transient. Очевидно, Jackson2 достаточно «умен», чтобы любезно игнорировать вещи, помеченные значком, @Transientесли вы явно не скажете этого не делать. Ключом было добавить модуль hibernate5 (который я использовал для работы с другими аннотациями Hibernate) и отключить эту USE_TRANSIENT_ANNOTATIONфункцию в моем приложении на Джерси:

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

Вот зависимость для Hibernate5Module:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-hibernate5</artifactId>
    <version>2.8.9</version>
</dependency>

После попытки каждого другого метода, упомянутого выше и других, @JsonProperty @JsonInclude @JsonSerialize + @JsonDeserialize mapper.configure(MapperFeature.PROPAGATE_TRANSIENT_MARKER, false));это решение, наконец, сработало. Спасибо!
Aceonline

1

Иногда вы хотите:

  1. Сериализация колонки
  2. Проигнорируйте колонку как сохраняющуюся:

использование @Column(name = "columnName", insertable = false, updatable = false)

Хороший сценарий - это когда определенный столбец автоматически вычисляется с использованием других значений столбца.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.