Usynkroniseret PersistenceContext – Sådan modelleres samtaler med JPA
De fleste Java EE-applikationer er statsløse, og der er gode grunde til det. Men ikke alle brugerinteraktioner kan implementeres på den måde. Nogle gange har du brug for at modellere en samtale, der består af flere trin, såsom den typiske indkøbskurv eller komplekse opdateringshandlinger.
At gøre dette med JPA 2.0 føltes altid en smule klodset. PersistenceContext var altid synkroniseret med transaktionen, og alle ændringer blev udbredt til databasen, så snart transaktionen blev begået. Derfor skulle forretningsniveauet håndtere samtalen og udføre alle ændringer i databasen i det sidste trin.
Dette blev meget nemmere med introduktionen af den usynkroniserede PersistenceContext i JPA 2.1. Som navnet allerede fortæller dig, er denne ikke synkroniseret med transaktionen, og ændringer overføres ikke til databasen, før joinTransaction() metode bliver kaldt. Derfor kan du oprette, ændre og slette enheder i hvert trin af samtalen uden at foretage nogen ændringer i databasen, indtil det sidste trin i samtalen slutter sig til transaktionen.
Hvis du vil lære mere om de andre funktioner introduceret i JPA 2.1, så tag et kig på JPA 2.1 – 12 funktioner, som enhver udvikler bør kende, og sørg for at downloade de nye funktioner i JPA 2.1 snydeark.
Eksempelenhederne
Før jeg kommer ind på detaljerne om usynkroniserede PersistenceContexts , lad os tage et kig på de enheder, jeg vil bruge i dette indlæg. Som du kan se i de følgende kodestykker, er begge entiteter ret enkle. Orden enhed har kun to egenskaber, et id og en liste af Vare s, og Varen enhed har et id egenskab, en reference til ordren det tilhører og navnet på et produkt .
@Entity(name="my_order") public class Order { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) @Column(name = "id", updatable = false, nullable = false) private Long id; @OneToMany(mappedBy = "order") private List<Item> items = new ArrayList<Item>(); ... }
@Entity public class Item { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id", updatable = false, nullable = false) private Long id; @Column private String product; @ManyToOne @JoinColumn(name = "fk_order") private Order order; ... }
Modellere samtalen
Jeg vil bruge disse to enheder til at modellere en simpel indkøbskurvsamtale. Orden oprettes, så snart brugeren begynder at browse på siden. Hver vare, hun lægger i indkøbskurven, tilføjes som en vare til ordren og alt overføres til databasen, så snart brugeren beslutter sig for at købe.
// 1. create the Order cart.start(); // 2. add one Item to the Order cart.addItem("myFirstProduct"); // 3. add additional Item to the Order cart.addItem("mySecondProduct"); // 4. write Order with its Items to the database cart.persist();
Så samtalen består af 4 forskellige anmodninger til den statelige ShoppingCart EJB, som opretter ordren , 2 Vare s og udløs persisten til databasen i den endelige anmodning.
@Stateful public class ShoppingCart implements ShoppingCartRemote { Logger log = Logger.getLogger(this.getClass().getSimpleName()); @PersistenceContext(synchronization = SynchronizationType.UNSYNCHRONIZED, type = PersistenceContextType.EXTENDED) EntityManager em; Order o; @Override public void start() { log.info("Create order"); this.o = new Order(); this.em.persist(o); } @Override public void addItem(String product) { log.info("Create item for product "+product); Item i = new Item(); i.setProduct(product); i.setOrder(o); o.getItems().add(i); this.em.persist(i); } @Override public void persist() { log.info("Join transaction and write changes to the database"); this.em.joinTransaction(); } }
Implementering af en sådan samtale med en usynkroniseret PersistenceContext er ret nemt. Som jeg forklarede i begyndelsen, den usynkroniserede PersistenceContext bliver ikke synkroniseret med en igangværende transaktion, før joinTransaction() metode bliver kaldt. Det betyder også, at ændringerne i PersistenceContext ikke blive udbredt til databasen, før den er forbundet med transaktionen. Så du kan oprette ordren enhed i den første anmodning og opret 2 element enheder i den anden og tredje uden at udbrede nogen ændringer i databasen. De nyoprettede enheder vil blive skrevet til databasen i den fjerde anmodning, som kalder joinTransaction() metode på EntityManager som slutter sig til PersistenceContext med den aktuelle transaktion.
Lad os se mere detaljeret på de forskellige trin og tjekke i logfilen, hvordan persistensudbyderen (i mit tilfælde Hibernate) interagerer med databasen.
Opret en usynkroniseret PersistenceContext
Den første ting du skal gøre er naturligvis at oprette den usynkroniserede PersistenceContext . Som sædvanligt i et Java EE-miljø kan du gøre dette ved at annotere en EntityManager egenskaben med @PersistenceContext annotation.
@PersistenceContext(synchronization = SynchronizationType.UNSYNCHRONIZED, type = PersistenceContextType.EXTENDED) EntityManager em;
Som du kan se i kodestykket, indstiller jeg synkroniseringen og skriv egenskaber på annotationen. Den første definerer, at denne PersistenceContext bør ikke synkroniseres med transaktionen, og den anden beholder PersistenceContext i live efter transaktionen er afsluttet. Dette er påkrævet, fordi hvert trin i samtalen vil køre inden for sin egen transaktion.
OK, nu kan vi bruge denne PersistenceContext at implementere samtalen.
Opret en ordre
Orden enhed bliver oprettet i den første anmodning ved at instansiere en Ordre enhed og leverer den til persist() metoden for EntityManager .
public void start() { log.info("Create order"); this.o = new Order(); this.em.persist(o); }
Som du kan se i logfilen, vælger Hibernate en ny værdi fra en sekvens for at initialisere den primære nøgle, men indsætter ikke ordreenheden i databasen.
14:30:17,454 INFO [ShoppingCart] (default task-1) Create order 14:30:17,464 DEBUG [org.hibernate.SQL] (default task-1) select nextval ('hibernate_sequence')
Tilføj varer til ordren
I det næste trin tilføjer brugeren to varer til ordren. Så den følgende metode, som kalder persist()-metoden for EntityManager med en nyoprettet Item-entitet, bliver kaldt to gange.
@Override public void addItem(String product) { log.info("Create item for product "+product); Item i = new Item(); i.setProduct(product); i.setOrder(o); o.getItems().add(i); this.em.persist(i); }
Outputtet i logfilen svarer til oprettelsen af ordreenheden. Hibernate vælger kun id-værdien fra sekvensen, men udbreder ikke de nye entiteter til databasen.
14:30:17,539 INFO [ShoppingCart] (default task-1) Create item for product myFirstProduct 14:30:17,540 DEBUG [org.hibernate.SQL] (default task-1) select nextval ('hibernate_sequence') 14:30:17,548 INFO [ShoppingCart] (default task-1) Create item for product mySecondProduct 14:30:17,548 DEBUG [org.hibernate.SQL] (default task-1) select nextval ('hibernate_sequence')
Formidl ændringerne
OK, brugeren beslutter sig nu for at købe indholdet af indkøbskurven, og vi skal fortsætte ordren og dens Vare s til databasen. Derfor PersistenceContext skal sluttes til den aktuelle transaktion ved at kalde joinTransaction() metode på EntityManager .
public void persist() { log.info("Join transaction and write changes to the database"); this.em.joinTransaction(); }
Som du kan se i logfilen, resulterer dette i 3 insert-sætninger, som udbreder de oprettede entiteter til databasen.
14:30:17,694 INFO [ShoppingCart] (default task-1) Join transaction and write changes to the database 14:30:17,728 DEBUG [org.hibernate.SQL] (default task-1) insert into my_order (id) values (?) 14:30:17,739 DEBUG [org.hibernate.SQL] (default task-1) insert into Item (fk_order, product, id) values (?, ?, ?) 14:30:17,743 DEBUG [org.hibernate.SQL] (default task-1) insert into Item (fk_order, product, id) values (?, ?, ?)
Oversigt
Som du har set i eksemplet, er den usynkroniserede PersistenceContext gør det meget nemmere at implementere samtaler med flere anmodninger. Der er ikke længere behov for at håndtere alt i forretningsniveauet og udføre ændringerne af alle trin under det sidste trin af samtalen. Du kan nu ændre enheder i hvert trin af samtalen, og databasen vil ikke blive opdateret før PersistenceContext slutter sig til transaktionen.
Hvad synes du om denne funktion? Har du allerede brugt det i et af dine projekter? Efterlad mig venligst en kommentar, jeg ville elske at høre om det.
Og glem ikke at downloade dine nye funktioner i JPA 2.1 snydeark. 😉