Який найпростіший спосіб ігнорувати поле 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.