Java >> Java tutorial >  >> Tag >> synchronized

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. 😉


No
Java tag