JPA - ส่งคืน id ที่สร้างขึ้นโดยอัตโนมัติหลังจากยังคงมีอยู่ ()


115

ฉันใช้ JPA (EclipseLink) และ Spring สมมติว่าฉันมีเอนทิตีธรรมดาที่มี ID ที่สร้างขึ้นอัตโนมัติ:

@Entity
public class ABC implements Serializable {
     @Id
     @GeneratedValue(strategy=GenerationType.IDENTITY)
     private int id;

     // ...
}

ในคลาส DAO ของฉันฉันมีวิธีการแทรกที่เรียกpersist()ใช้เอนทิตีนี้ ฉันต้องการให้เมธอดส่งคืน ID ที่สร้างขึ้นสำหรับเอนทิตีใหม่ แต่เมื่อฉันทดสอบมันจะส่งคืน0แทน

public class ABCDao {
    @PersistenceContext
    EntityManager em;

    @Transactional(readOnly=false)
    public int insertABC(ABC abc) {
         em.persist(abc);
         // I WANT TO RETURN THE AUTO-GENERATED ID OF abc
         // HOW CAN I DO IT?
         return abc.id; // ???
    }
}

ฉันยังมีคลาสบริการที่ครอบคลุม DAO หากนั่นสร้างความแตกต่าง:

public class ABCService {
    @Resource(name="ABCDao")
    ABCDao abcDao;

    public int addNewABC(ABC abc) {
         return abcDao.insertABC(abc);
    }
}

ที่คล้ายกันสามารถอ้างอิงstackoverflow.com/q/3328813/366964
Nayan Wadekar

ขอบคุณสำหรับคำตอบ และเป็นวิธีแก้ปัญหาที่ยุ่งยาก (ไม่ใช่ JPA) เราสามารถใช้รหัสเฉพาะอื่นเช่นการประทับเวลา unix
sura2k

1
เป็นไปได้ที่จะทำซ้ำเมื่อ JPA ตั้งค่า @GeneratedValue @Id
Raedwald

คำตอบ:


186

ID รับประกันว่าจะสร้างขึ้นในเวลาล้างเท่านั้น การคงอยู่ของเอนทิตีทำให้ "แนบ" กับบริบทการคงอยู่เท่านั้น ดังนั้นให้ล้างตัวจัดการเอนทิตีอย่างชัดเจน:

em.persist(abc);
em.flush();
return abc.getId();

หรือส่งคืนเอนทิตีเองแทนที่จะเป็น ID เมื่อธุรกรรมสิ้นสุดลงการฟลัชจะเกิดขึ้นและผู้ใช้ของหน่วยงานภายนอกธุรกรรมจะเห็นรหัสที่สร้างขึ้นในเอนทิตี

@Override
public ABC addNewABC(ABC abc) {
    abcDao.insertABC(abc);
    return abc;
}

10
หมายเหตุ: สิ่งนี้จำเป็นต้องใส่คำอธิบายในฟิลด์ id ด้วย@GeneratedValue- อะไรก็ตามที่เกี่ยวข้อง
Mr_and_Mrs_D

คุณช่วยอธิบายปัญหาในการพยายามบรรลุสิ่งนี้ด้วยรหัสคอมโพสิตstackoverflow.com/questions/31362100/…
bl3e

มีโทษประสิทธิภาพ (หรือผลเสียอื่น ๆ ) จากการล้างด้วยตนเองหลังจากยังคงมีอยู่หรือไม่?
Craig Otis

3
ใช่มี: การไปกลับที่ไม่จำเป็นไปยังฐานข้อมูลหากธุรกรรมสิ้นสุดลงด้วยการย้อนกลับข้อยกเว้นที่อาจเกิดขึ้นหากเอนทิตีที่ยังคงอยู่ (หรือเอนทิตีล้างอื่น ๆ ) ยังไม่อยู่ในสถานะที่ถูกต้อง ลำดับหรือตัวสร้าง uuid นั้นง่ายกว่าและมีประสิทธิภาพมากกว่าและไม่มีปัญหาเหล่านี้เนื่องจาก ID ถูกสร้างและกำหนดก่อนที่เอนทิตีจะถูกเขียนไปยังฐานข้อมูล
JB Nizet

1
@JBNizet คุณต้องส่งคืนอินสแตนซ์หรือไม่หรือการอ้างอิงที่ผ่านยังใช้ได้ ฉันหมายถึงinsertABCสร้างวัตถุใหม่หรือไม่? หรือดัดแปลงของเก่า?
ryvantage

13
@Entity
public class ABC implements Serializable {
     @Id
     @GeneratedValue(strategy=GenerationType.IDENTITY)
     private int id;   
}

ตรวจสอบว่าสัญกรณ์ @GeneratedValue มีอยู่ในคลาสเอนทิตีของคุณสิ่งนี้จะบอก JPA เกี่ยวกับพฤติกรรมที่สร้างขึ้นโดยอัตโนมัติของคุณสมบัติเอนทิตีของคุณ


4

นี่คือวิธีที่ฉันทำ:

EntityManager entityManager = getEntityManager();
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
entityManager.persist(object);
transaction.commit();
long id = object.getId();
entityManager.close();

มันไม่ทำงานโดยให้ศูนย์เป็นผลตอบแทนหลังจากเก็บข้อมูลไว้ในตาราง ทั้งรีเฟรชไม่ทำงานในกรณีนี้ .. ฉันควรทำอย่างไรสำหรับสิ่งนี้ กรุณาแนะนำวิธี ... ขอบคุณ
Vikrant Kashyap

1
@VikrantKashyap กรุณาโพสต์คำถามใหม่พร้อมรหัสขนาดเล็กและพูดถึงฉันเพื่อที่ฉันจะได้ดู
Koray Tugay

2

คุณยังสามารถใช้ GenerationType.TABLE แทน IDENTITY ได้ซึ่งจะใช้ได้เฉพาะหลังจากการแทรก


2
เพียงคำเตือน เมื่อฉันลอง GenerationType.TABLE มันสร้างตารางแยกต่างหากชื่อ hibernate_sequences และเริ่มต้นลำดับใหม่จาก 1
SriSri

0

ตัวเลือกอื่นที่เข้ากันได้กับ 4.0:

ก่อนที่จะดำเนินการเปลี่ยนแปลงคุณสามารถกู้คืนCayenneDataObjectอ็อบเจ็กต์ใหม่จากคอลเล็กชันที่เกี่ยวข้องกับบริบทดังต่อไปนี้:

CayenneDataObject dataObjectsCollection = (CayenneDataObject)cayenneContext.newObjects();

จากนั้นเข้าถึงObjectIdแต่ละรายการในคอลเล็กชันเช่น:

ObjectId objectId = dataObject.getObjectId();

ในที่สุดคุณสามารถวนซ้ำภายใต้ค่าโดยที่โดยปกติแล้ว -id ที่สร้างขึ้นจะเป็นค่าแรก (สำหรับคีย์คอลัมน์เดียว) ในแผนที่ที่ส่งคืนโดยgetIdSnapshot()จะมีชื่อคอลัมน์ที่เกี่ยวข้องกับ PK ด้วย เป็นคีย์:

objectId.getIdSnapshot().values()

0

นี่คือวิธีที่ฉันทำ คุณสามารถลอง

    public class ABCService {
    @Resource(name="ABCDao")
    ABCDao abcDao;

    public int addNewABC(ABC abc) {
         ABC.setId(0);
         return abcDao.insertABC(abc);
    }
}

-3
em.persist(abc);
em.refresh(abc);
return abc;

วิธีนี้ไม่ได้ผลสำหรับฉัน ได้รับข้อผิดพลาดนี้: javax.persistence.PersistenceException: org.hibernate.HibernateException: อินสแตนซ์นี้ยังไม่มีอยู่เป็นแถวในฐานข้อมูล]
rtcarlson

@rtcarlson ใช่มันจะไม่ทำงาน ถ้าคุณกำลังสร้างวัตถุใหม่สิ่งที่คุณต้องการไม่ได้em.flush() em.refresh(abc)
Ibrahim Dauda
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.