Який найпростіший спосіб ігнорувати поле JPA під час наполегливості?


281

Я по суті шукаю анотацію типу "@Ignore", за допомогою якої я можу не допустити збереження певного поля. Як цього можна досягти?

Відповіді:


450

@Transient відповідає вашим потребам.


20
Але тоді Джексон не буде серіалізувати поле при переході на JSON ... як вирішити?
— MobileMon

це залежить від вашої програми. якщо ви коментуєте свій клас сутності - він застосовується скрізь; але якщо ви анотуєте дао, що використовує сутність - це вже інша історія. коротше: використовуйте DAO, коли у вас є кілька сховищ
— Андрій Плотніков

Не працює у полях List. Для полів типу Колекція сплячий фактично створює нову проміжну таблицю. Будь-яке рішення для цього?
— zulkarnain shah

@MobileMon Ви можете вирішити, перевірити мою відповідь stackoverflow.com/a/41850392/3871754
— Каміль Nekanowicz

@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
Я зробив це у весняному черевику
— Каміль Неканович

привіт, я намагаюся це використати, але це не серіалізується з полем @Transient
— Мохаммед

@Mohammad додати анотацію @JsonInclude ()
— Kamil Nekanowicz

Спробував це у Spring Boot 2.1.7, але не вийшло. Але все ж ви отримуєте мій голос, оскільки він може працювати в інших сценаріях.
— Aditya Ekbote


19

Ця відповідь приходить трохи пізно, але вона завершує відповідь.

Для того, щоб уникнути поля сутності, що має зберігатися в БД, можна використовувати один з двох механізмів:

@Transient - анотація JPA, що позначає поле як нестабільне

перехідне ключове слово в java. Остерігайтеся - використання цього ключового слова запобігає використанню поля з будь-яким механізмом серіалізації від Java. Отже, якщо поле має бути серіалізовано, краще використовувати лишеанотацію @Transient .


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

@xtiger ви можете створити спеціальний запит для архівації цієї функції. Ось приклад посилання
— Mohale

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таблиці:

Таблиці публікації та публікації

Ви хочете зіставити 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для асоціації, яку ви хочете ігнорувати в сплячку.

Наприклад, ми можемо зіставити попередню асоціацію один на один так:

@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. Мабуть, Джексон2 досить 'розумний', щоб люб’язно ігнорувати позначені речі, @Transientякщо ви прямо не скажете цього. Ключовим моментом було додати модуль hibernate5 (який я використовував для вирішення інших анотацій зі сплячки) та відключити 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));це рішення нарешті спрацювало. Дякую!
— Асеонлайн

1

Іноді хочеться:

  1. Серіалізуйте стовпець
  2. Ігнорувати стовпець від збереження:

Використовуйте @Column(name = "columnName", insertable = false, updatable = false)

Хороший сценарій - коли певний стовпець автоматично розраховується за допомогою інших значень стовпців

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.