Trình tự JPA ngủ đông (không phải Id)


138

Có thể sử dụng chuỗi DB cho một số cột không phải là mã định danh / không phải là một phần của mã định danh hỗn hợp ?

Tôi đang sử dụng hibernate làm nhà cung cấp jpa và tôi có một bảng có một số cột được tạo giá trị (sử dụng chuỗi), mặc dù chúng không phải là một phần của mã định danh.

Điều tôi muốn là sử dụng một chuỗi để tạo một giá trị mới cho một thực thể, trong đó cột cho chuỗi đó KHÔNG phải là một phần của khóa chính:

@Entity
@Table(name = "MyTable")
public class MyEntity {

    //...
    @Id //... etc
    public Long getId() {
        return id;
    }

   //note NO @Id here! but this doesn't work...
    @GeneratedValue(strategy = GenerationType.AUTO, generator = "myGen")
    @SequenceGenerator(name = "myGen", sequenceName = "MY_SEQUENCE")
    @Column(name = "SEQ_VAL", unique = false, nullable = false, insertable = true, updatable = true)
    public Long getMySequencedValue(){
      return myVal;
    }

}

Sau đó, khi tôi làm điều này:

em.persist(new MyEntity());

id sẽ được tạo, nhưng thuộc mySequenceValtính cũng sẽ được tạo bởi nhà cung cấp JPA của tôi.

Chỉ cần làm cho mọi thứ rõ ràng: Tôi muốn Hibernate tạo ra giá trị cho mySequencedValuetài sản. Tôi biết Hibernate có thể xử lý các giá trị do cơ sở dữ liệu tạo ra, nhưng tôi không muốn sử dụng trình kích hoạt hoặc bất kỳ thứ gì khác ngoài chính Hibernate để tạo giá trị cho thuộc tính của mình. Nếu Hibernate có thể tạo giá trị cho khóa chính, tại sao nó không thể tạo cho một thuộc tính đơn giản?

Câu trả lời:


76

Tìm kiếm câu trả lời cho vấn đề này, tôi tình cờ thấy liên kết này

Có vẻ như Hibernate / JPA không thể tự động tạo giá trị cho các thuộc tính không id của bạn. Các @GeneratedValuechú thích chỉ được sử dụng kết hợp với @Idđể tạo auto-số.

Các @GeneratedValuechú thích chỉ nói với Hibernate rằng cơ sở dữ liệu được tạo ra giá trị này chính nó.

Giải pháp (hoặc giải quyết vấn đề) được đề xuất trong diễn đàn đó là tạo một thực thể riêng biệt với Id được tạo, đại loại như thế này:

@Entity
lớp chung GeneralSequenceNumber {
  @Tôi
  @GeneratedValue (...)
  số riêng dài;
}

@Entity 
lớp học công khai MyEntity {
  @Tôi ..
  id dài riêng;

  @OneToOne (...)
  riêng GeneralSequnceNumber myVal;
}

Từ tài liệu java của @GeneratedValue: "Chú thích GeneratedValue có thể được áp dụng cho thuộc tính khóa chính hoặc trường của một thực thể hoặc siêu lớp được ánh xạ kết hợp với chú thích Id"
Kariem

11
Tôi thấy rằng @Column (cộtDefDef = "serial") hoạt động hoàn hảo nhưng chỉ dành cho PostgreQuery. Đối với tôi đây là giải pháp hoàn hảo, bởi vì thực thể thứ hai là tùy chọn "xấu xí"
Sergey Vedernikov

@SergeyVedernikov cực kỳ hữu ích. Bạn có phiền đăng bài đó như một câu trả lời riêng biệt không? Nó giải quyết vấn đề của tôi rất rất đơn giản và hiệu quả.
Matt Ball

@MattBall tôi đã đăng bài này dưới dạng câu trả lời riêng :) stackoverflow.com/a/10647933/620858
Sergey Vedernikov

1
Tôi đã mở một đề xuất cho phép @GeneratedValuetrên các trường không phải là id. Vui lòng bỏ phiếu để được bao gồm trong 2.2 java.net/jira/browse/JPAinksEC-113
Petar Tahchiev

44

Tôi thấy rằng nó @Column(columnDefinition="serial")hoạt động hoàn hảo nhưng chỉ dành cho PostgreSQL. Đối với tôi đây là giải pháp hoàn hảo, bởi vì thực thể thứ hai là tùy chọn "xấu xí".


Xin chào, tôi cần một lời giải thích về nó. Bạn có thể cho tôi biết thêm xin vui lòng?
Emaborsa

2
@Emaborsa columnDefinition=Về cơ bản, bit nói với Hiberate không cố gắng tạo định nghĩa cột và thay vào đó sử dụng văn bản bạn đã cung cấp. Về cơ bản, DDL của bạn cho cột theo nghĩa đen sẽ chỉ là tên + cộtDefDef. Trong trường hợp này (PostgreSQL), mycolumn seriallà một cột hợp lệ trong bảng.
Patrick

7
Tương đương với MySQL là@Column(columnDefinition = "integer auto_increment")
Richard Kennard

2
Liệu tự động này tạo ra giá trị của nó? Tôi đã cố gắng duy trì một thực thể với định nghĩa trường như thế này nhưng nó không tạo ra giá trị. nó đã ném một giá trị null trong cột <cột> vi phạm ràng buộc không null
KyelJmD

7
Tôi đã sử dụng @Column(insertable = false, updatable = false, columnDefinition="serial")để ngăn ngủ đông cố gắng chèn giá trị null hoặc cập nhật trường. Sau đó, bạn cần truy vấn lại db để lấy id được tạo sau khi chèn nếu bạn cần sử dụng ngay lập tức.
Robert Di Paolo

20

Tôi biết đây là một câu hỏi rất cũ, nhưng nó được thể hiện trước tiên dựa trên kết quả và jpa đã thay đổi rất nhiều kể từ câu hỏi.

Cách đúng đắn để làm điều đó bây giờ là với @Generatedchú thích. Bạn có thể xác định chuỗi, đặt mặc định trong cột thành chuỗi đó và sau đó ánh xạ cột là:

@Generated(GenerationTime.INSERT)
@Column(name = "column_name", insertable = false)

1
Điều này vẫn đòi hỏi giá trị được tạo ra bởi cơ sở dữ liệu, không thực sự trả lời câu hỏi. Đối với cơ sở dữ liệu Oracle trước 12c, bạn vẫn cần phải viết trình kích hoạt cơ sở dữ liệu để tạo giá trị.
Bernie

9
Ngoài ra, đây là một chú thích Hibernate, không phải JPA.
caarlos0

14

Hibernate chắc chắn hỗ trợ này. Từ các tài liệu:

"Các thuộc tính được tạo là các thuộc tính có các giá trị được tạo bởi cơ sở dữ liệu. Thông thường, các ứng dụng Hibernate cần để làm mới các đối tượng có chứa bất kỳ thuộc tính nào mà cơ sở dữ liệu đang tạo giá trị. Về cơ bản, bất cứ khi nào Hibernate phát hành SQL INSERT hoặc UPDATE cho một thực thể đã xác định các thuộc tính được tạo, nó sẽ ngay lập tức đưa ra một lựa chọn sau đó để lấy các giá trị được tạo. "

Đối với các thuộc tính được tạo khi chỉ chèn, ánh xạ thuộc tính của bạn (.hbm.xml) sẽ trông như sau:

<property name="foo" generated="insert"/>

Đối với các thuộc tính được tạo khi chèn và cập nhật ánh xạ thuộc tính của bạn (.hbm.xml) sẽ trông như sau:

<property name="foo" generated="always"/>

Thật không may, tôi không biết JPA, vì vậy tôi không biết tính năng này có bị lộ qua JPA không (tôi nghi ngờ có thể không)

Ngoài ra, bạn sẽ có thể loại trừ thuộc tính khỏi các phần chèn và cập nhật, sau đó gọi thủ công session.refresh (obj); sau khi bạn đã chèn / cập nhật nó để tải giá trị được tạo từ cơ sở dữ liệu.

Đây là cách bạn sẽ loại trừ thuộc tính khỏi được sử dụng trong các câu lệnh chèn và cập nhật:

<property name="foo" update="false" insert="false"/>

Một lần nữa, tôi không biết nếu JPA phơi bày các tính năng Hibernate này, nhưng Hibernate có hỗ trợ chúng không.


1
Chú thích @Generated tương ứng với cấu hình XML ở trên. Xem phần này của tài liệu ngủ đông để biết thêm chi tiết.
Eric

8

Theo dõi đây là cách tôi làm cho nó hoạt động:

@Override public Long getNextExternalId() {
    BigDecimal seq =
        (BigDecimal)((List)em.createNativeQuery("select col_msd_external_id_seq.nextval from dual").getResultList()).get(0);
    return seq.longValue();
}

Một biến thể với Hibernate 4.2.19 và orory: SQLQuery sqlQuery = getSession().createSQLQuery("select NAMED_SEQ.nextval seq from dual"); sqlQuery.addScalar("seq", LongType.INSTANCE); return (Long) sqlQuery.uniqueResult();
Aaron

6

Tôi đã sửa lỗi tạo UUID (hoặc chuỗi) bằng Hibernate bằng cách sử dụng @PrePersistchú thích:

@PrePersist
public void initializeUUID() {
    if (uuid == null) {
        uuid = UUID.randomUUID().toString();
    }
}

5

Mặc dù đây là một chủ đề cũ tôi muốn chia sẻ giải pháp của mình và hy vọng nhận được một số phản hồi về điều này. Được cảnh báo rằng tôi chỉ thử nghiệm giải pháp này với cơ sở dữ liệu cục bộ của mình trong một số testcase JUnit. Vì vậy, đây không phải là một tính năng sản xuất cho đến nay.

Tôi đã giải quyết vấn đề đó cho tôi bằng cách giới thiệu một chú thích tùy chỉnh có tên Sequence không có thuộc tính. Nó chỉ là một điểm đánh dấu cho các trường nên được gán một giá trị từ một chuỗi tăng dần.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Sequence
{
}

Sử dụng chú thích này tôi đã đánh dấu các thực thể của tôi.

public class Area extends BaseEntity implements ClientAware, IssuerAware
{
    @Column(name = "areaNumber", updatable = false)
    @Sequence
    private Integer areaNumber;
....
}

Để giữ cho cơ sở dữ liệu độc lập, tôi đã giới thiệu một thực thể có tên SequenceNumber chứa giá trị hiện tại của chuỗi và kích thước gia tăng. Tôi đã chọn className làm khóa duy nhất để mỗi lớp thực thể sẽ có chuỗi riêng.

@Entity
@Table(name = "SequenceNumber", uniqueConstraints = { @UniqueConstraint(columnNames = { "className" }) })
public class SequenceNumber
{
    @Id
    @Column(name = "className", updatable = false)
    private String className;

    @Column(name = "nextValue")
    private Integer nextValue = 1;

    @Column(name = "incrementValue")
    private Integer incrementValue = 10;

    ... some getters and setters ....
}

Bước cuối cùng và khó khăn nhất là PreInsertListener xử lý việc gán số thứ tự. Lưu ý rằng tôi đã sử dụng mùa xuân như container đậu.

@Component
public class SequenceListener implements PreInsertEventListener
{
    private static final long serialVersionUID = 7946581162328559098L;
    private final static Logger log = Logger.getLogger(SequenceListener.class);

    @Autowired
    private SessionFactoryImplementor sessionFactoryImpl;

    private final Map<String, CacheEntry> cache = new HashMap<>();

    @PostConstruct
    public void selfRegister()
    {
        // As you might expect, an EventListenerRegistry is the place with which event listeners are registered
        // It is a service so we look it up using the service registry
        final EventListenerRegistry eventListenerRegistry = sessionFactoryImpl.getServiceRegistry().getService(EventListenerRegistry.class);

        // add the listener to the end of the listener chain
        eventListenerRegistry.appendListeners(EventType.PRE_INSERT, this);
    }

    @Override
    public boolean onPreInsert(PreInsertEvent p_event)
    {
        updateSequenceValue(p_event.getEntity(), p_event.getState(), p_event.getPersister().getPropertyNames());

        return false;
    }

    private void updateSequenceValue(Object p_entity, Object[] p_state, String[] p_propertyNames)
    {
        try
        {
            List<Field> fields = ReflectUtil.getFields(p_entity.getClass(), null, Sequence.class);

            if (!fields.isEmpty())
            {
                if (log.isDebugEnabled())
                {
                    log.debug("Intercepted custom sequence entity.");
                }

                for (Field field : fields)
                {
                    Integer value = getSequenceNumber(p_entity.getClass().getName());

                    field.setAccessible(true);
                    field.set(p_entity, value);
                    setPropertyState(p_state, p_propertyNames, field.getName(), value);

                    if (log.isDebugEnabled())
                    {
                        LogMF.debug(log, "Set {0} property to {1}.", new Object[] { field, value });
                    }
                }
            }
        }
        catch (Exception e)
        {
            log.error("Failed to set sequence property.", e);
        }
    }

    private Integer getSequenceNumber(String p_className)
    {
        synchronized (cache)
        {
            CacheEntry current = cache.get(p_className);

            // not in cache yet => load from database
            if ((current == null) || current.isEmpty())
            {
                boolean insert = false;
                StatelessSession session = sessionFactoryImpl.openStatelessSession();
                session.beginTransaction();

                SequenceNumber sequenceNumber = (SequenceNumber) session.get(SequenceNumber.class, p_className);

                // not in database yet => create new sequence
                if (sequenceNumber == null)
                {
                    sequenceNumber = new SequenceNumber();
                    sequenceNumber.setClassName(p_className);
                    insert = true;
                }

                current = new CacheEntry(sequenceNumber.getNextValue() + sequenceNumber.getIncrementValue(), sequenceNumber.getNextValue());
                cache.put(p_className, current);
                sequenceNumber.setNextValue(sequenceNumber.getNextValue() + sequenceNumber.getIncrementValue());

                if (insert)
                {
                    session.insert(sequenceNumber);
                }
                else
                {
                    session.update(sequenceNumber);
                }
                session.getTransaction().commit();
                session.close();
            }

            return current.next();
        }
    }

    private void setPropertyState(Object[] propertyStates, String[] propertyNames, String propertyName, Object propertyState)
    {
        for (int i = 0; i < propertyNames.length; i++)
        {
            if (propertyName.equals(propertyNames[i]))
            {
                propertyStates[i] = propertyState;
                return;
            }
        }
    }

    private static class CacheEntry
    {
        private int current;
        private final int limit;

        public CacheEntry(final int p_limit, final int p_current)
        {
            current = p_current;
            limit = p_limit;
        }

        public Integer next()
        {
            return current++;
        }

        public boolean isEmpty()
        {
            return current >= limit;
        }
    }
}

Như bạn có thể thấy từ đoạn mã trên, người nghe đã sử dụng một thể hiện SequenceNumber cho mỗi lớp thực thể và bảo lưu một vài số thứ tự được xác định bởi gia sốValue của thực thể SequenceNumber. Nếu nó hết số thứ tự, nó sẽ tải thực thể SequenceNumber cho lớp đích và dự trữ các giá trị gia tăng giá trị cho các cuộc gọi tiếp theo. Bằng cách này, tôi không cần truy vấn cơ sở dữ liệu mỗi khi cần một giá trị chuỗi. Lưu ý StatlessSession đang được mở để đặt bộ số thứ tự tiếp theo. Bạn không thể sử dụng cùng một phiên mà thực thể đích hiện đang tồn tại vì điều này sẽ dẫn đến một concExModificationException trong EntityPersister.

Hy vọng điều này sẽ giúp được ai đó.


5

Nếu bạn đang sử dụng postgresql
Và tôi đang sử dụng trong spring boot 1.5.6

@Column(columnDefinition = "serial")
@Generated(GenerationTime.INSERT)
private Integer orderID;

1
Nó cũng hoạt động với tôi, tôi đang sử dụng spring boot 2.1.6.RELEASE, Hibernate 5.3.10.Final, Ngoài những gì đã chỉ ra, tôi phải tạo một sự bí mật seq_ordervà tạo mẫu tham chiếu cho trường, nextval('seq_order'::regclass)
OJVM

3

Tôi chạy trong tình huống tương tự như bạn và tôi cũng không tìm thấy bất kỳ câu trả lời nghiêm túc nào nếu về cơ bản có thể tạo ra các tài sản không có id với JPA hay không.

Giải pháp của tôi là gọi chuỗi bằng truy vấn JPA gốc để đặt thuộc tính bằng tay trước khi thuyết phục nó.

Điều này không thỏa mãn nhưng nó hoạt động như một cách giải quyết cho thời điểm này.

Mario


2

Tôi đã tìm thấy ghi chú cụ thể này trong phiên 9.1.9 Chú thích GeneratedValue từ thông số kỹ thuật của JPA: "[43] Các ứng dụng di động không nên sử dụng chú thích GeneratedValue trên các trường hoặc thuộc tính liên tục khác." Vì vậy, tôi cho rằng không thể tự động tạo giá trị cho các giá trị khóa không chính ít nhất là sử dụng JPA đơn giản.


1

Có vẻ như chủ đề đã cũ, tôi chỉ muốn thêm giải pháp của mình vào đây (Sử dụng AspectJ - AOP vào mùa xuân).

Giải pháp là tạo một chú thích tùy chỉnh @InjectSequenceValuenhư sau.

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface InjectSequenceValue {
    String sequencename();
}

Bây giờ bạn có thể chú thích bất kỳ trường nào trong thực thể, để giá trị trường bên dưới (Long / Integer) sẽ được chèn vào thời gian chạy bằng cách sử dụng giá trị tiếp theo của chuỗi.

Chú thích như thế này.

//serialNumber will be injected dynamically, with the next value of the serialnum_sequence.
 @InjectSequenceValue(sequencename = "serialnum_sequence") 
  Long serialNumber;

Cho đến nay chúng ta đã đánh dấu trường chúng ta cần tiêm giá trị chuỗi. Vì vậy, chúng ta sẽ xem cách tiêm giá trị chuỗi vào các trường được đánh dấu, điều này được thực hiện bằng cách tạo điểm cắt trong AspectJ.

Chúng tôi sẽ kích hoạt tiêm ngay trước khi save/persistphương thức được thực thi. Điều này được thực hiện trong lớp dưới đây.

@Aspect
@Configuration
public class AspectDefinition {

    @Autowired
    JdbcTemplate jdbcTemplate;


    //@Before("execution(* org.hibernate.session.save(..))") Use this for Hibernate.(also include session.save())
    @Before("execution(* org.springframework.data.repository.CrudRepository.save(..))") //This is for JPA.
    public void generateSequence(JoinPoint joinPoint){

        Object [] aragumentList=joinPoint.getArgs(); //Getting all arguments of the save
        for (Object arg :aragumentList ) {
            if (arg.getClass().isAnnotationPresent(Entity.class)){ // getting the Entity class

                Field[] fields = arg.getClass().getDeclaredFields();
                for (Field field : fields) {
                    if (field.isAnnotationPresent(InjectSequenceValue.class)) { //getting annotated fields

                        field.setAccessible(true); 
                        try {
                            if (field.get(arg) == null){ // Setting the next value
                                String sequenceName=field.getAnnotation(InjectSequenceValue.class).sequencename();
                                long nextval=getNextValue(sequenceName);
                                System.out.println("Next value :"+nextval); //TODO remove sout.
                                field.set(arg, nextval);
                            }

                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }

        }
    }

    /**
     * This method fetches the next value from sequence
     * @param sequence
     * @return
     */

    public long getNextValue(String sequence){
        long sequenceNextVal=0L;

        SqlRowSet sqlRowSet= jdbcTemplate.queryForRowSet("SELECT "+sequence+".NEXTVAL as value FROM DUAL");
        while (sqlRowSet.next()){
            sequenceNextVal=sqlRowSet.getLong("value");

        }
        return  sequenceNextVal;
    }
}

Bây giờ bạn có thể chú thích bất kỳ Thực thể như dưới đây.

@Entity
@Table(name = "T_USER")
public class UserEntity {

    @Id
    @SequenceGenerator(sequenceName = "userid_sequence",name = "this_seq")
    @GeneratedValue(strategy = GenerationType.SEQUENCE,generator = "this_seq")
    Long id;
    String userName;
    String password;

    @InjectSequenceValue(sequencename = "serialnum_sequence") // this will be injected at the time of saving.
    Long serialNumber;

    String name;
}

0

"Tôi không muốn sử dụng trình kích hoạt hoặc bất kỳ thứ gì khác ngoài chính Hibernate để tạo giá trị cho tài sản của mình"

Trong trường hợp đó, làm thế nào về việc tạo một triển khai UserType để tạo ra giá trị bắt buộc và định cấu hình siêu dữ liệu để sử dụng UserType đó để duy trì thuộc tính mySequenceVal?


0

Điều này không giống như sử dụng một chuỗi. Khi sử dụng một chuỗi, bạn không chèn hoặc cập nhật bất cứ điều gì. Bạn chỉ đơn giản là lấy ra giá trị chuỗi tiếp theo. Có vẻ như ngủ đông không hỗ trợ nó.


0

Nếu bạn có một cột với loại UNIQUEIDENTIFIER và cần tạo mặc định khi chèn nhưng cột không phải là PK

@Generated(GenerationTime.INSERT)
@Column(nullable = false , columnDefinition="UNIQUEIDENTIFIER")
private String uuidValue;

Trong db bạn sẽ có

CREATE TABLE operation.Table1
(
    Id         INT IDENTITY (1,1)               NOT NULL,
    UuidValue  UNIQUEIDENTIFIER DEFAULT NEWID() NOT NULL)

Trong trường hợp này, bạn sẽ không xác định trình tạo cho một giá trị mà bạn cần (Nó sẽ tự động nhờ columnDefinition="UNIQUEIDENTIFIER"). Tương tự bạn có thể thử cho các loại cột khác


0

Tôi đã tìm thấy một cách giải quyết cho vấn đề này trên cơ sở dữ liệu MySql bằng cách sử dụng @PostConstruct và JdbcTemplate trong một ứng dụng Spring. Nó có thể thực hiện được với các cơ sở dữ liệu khác nhưng trường hợp sử dụng mà tôi sẽ trình bày dựa trên kinh nghiệm của tôi với MySql, vì nó sử dụng auto_increment.

Đầu tiên, tôi đã thử xác định một cột là auto_increment bằng thuộc tính CộtDefDef của chú thích @Column, nhưng nó không hoạt động vì cột cần phải là khóa để tự động tăng, nhưng rõ ràng cột sẽ không được xác định là một chỉ mục cho đến sau khi nó được xác định, gây ra bế tắc.

Đây là nơi tôi nảy ra ý tưởng tạo cột mà không có định nghĩa auto_increment và thêm nó vào sau cơ sở dữ liệu được tạo. Điều này có thể sử dụng chú thích @PostConstruct, khiến cho một phương thức được gọi ngay sau khi ứng dụng đã khởi tạo các bean, kết hợp với phương thức cập nhật của JdbcTemplate.

Mã như sau:

Trong thực thể của tôi:

@Entity
@Table(name = "MyTable", indexes = { @Index(name = "my_index", columnList = "mySequencedValue") })
public class MyEntity {
    //...
    @Column(columnDefinition = "integer unsigned", nullable = false, updatable = false, insertable = false)
    private Long mySequencedValue;
    //...
}

Trong một lớp PostConstructComponent:

@Component
public class PostConstructComponent {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @PostConstruct
    public void makeMyEntityMySequencedValueAutoIncremental() {
        jdbcTemplate.update("alter table MyTable modify mySequencedValue int unsigned auto_increment");
    }
}

0

Tôi muốn cung cấp một giải pháp thay thế bên cạnh giải pháp được chấp nhận của @Morten Berg, giải pháp tốt hơn cho tôi.

Cách tiếp cận này cho phép xác định trường với loại thực sự mong muốn Number- Longtrong trường hợp sử dụng của tôi - thay vì GeneralSequenceNumber. Điều này có thể hữu ích, ví dụ cho tuần tự hóa JSON (de-).

Nhược điểm là nó đòi hỏi một chút chi phí cơ sở dữ liệu.


Đầu tiên, chúng ta cần một ActualEntityloại mà chúng ta muốn tự động tăng generatedloại Long:

// ...
@Entity
public class ActualEntity {

    @Id 
    // ...
    Long id;

    @Column(unique = true, updatable = false, nullable = false)
    Long generated;

    // ...

}

Tiếp theo, chúng ta cần một thực thể trợ giúp Generated. Tôi đặt gói riêng tư bên cạnh ActualEntity, để giữ cho nó một chi tiết triển khai của gói:

@Entity
class Generated {

    @Id
    @GeneratedValue(strategy = SEQUENCE, generator = "seq")
    @SequenceGenerator(name = "seq", initialValue = 1, allocationSize = 1)
    Long id;

}

Cuối cùng, chúng ta cần một nơi để móc vào ngay trước khi chúng ta lưu ActualEntity. Ở đó, chúng tôi tạo và tồn tại một Generatedví dụ. Điều này sau đó cung cấp một chuỗi cơ sở dữ liệu được tạo ra idcủa loại Long. Chúng tôi sử dụng giá trị này bằng cách viết nó vàoActualEntity.generated .

Trong trường hợp sử dụng của mình, tôi đã triển khai điều này bằng cách sử dụng Spring Data REST @RepositoryEventHandler, được gọi ngay trước khi ActualEntitynhận được. Nó cần thể hiện nguyên tắc:

@Component
@RepositoryEventHandler
public class ActualEntityHandler {

    @Autowired
    EntityManager entityManager;

    @Transactional
    @HandleBeforeCreate
    public void generate(ActualEntity entity) {
        Generated generated = new Generated();

        entityManager.persist(generated);
        entity.setGlobalId(generated.getId());
        entityManager.remove(generated);
    }

}

Tôi đã không kiểm tra nó trong một ứng dụng thực tế, vì vậy hãy cẩn thận.


-1

Tôi đã ở trong một tình huống như bạn (chuỗi JPA / Hibernate cho trường không @Id) và cuối cùng tôi đã tạo một trình kích hoạt trong lược đồ db của mình để thêm số thứ tự duy nhất khi chèn. Tôi chỉ không bao giờ có nó để làm việc với JPA / Hibernate


-1

Sau khi dành hàng giờ, điều này giúp tôi giải quyết vấn đề của mình một cách gọn gàng:

Đối với Oracle 12c:

ID NUMBER GENERATED as IDENTITY

Đối với H2:

ID BIGINT GENERATED as auto_increment

Cũng làm:

@Column(insertable = false)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.