Hibernate实战(第二版)笔记----第三章--域模型与元数据

本章主要讲了域模型和元数据,以一个CaveatEmptor在线拍卖系统为例讲解了从项目架构分层,分析业务域,域模型,域模型的持久化类和持久化层之间的关注点完全分离,持久化类不清楚-且不依赖-持久化机制,然后讲解了域模型域元数据的关系,以及在运行时的方法方式。

域模型:被创建的实体类与属性被称为域模型.

元数据:即告知Hibernate持久化类及其属性与数据库表和列关联起来的方式。


JPA提供了两个元数据选项:

1、注解

2、XML描述符

    

JPA提供的访问持久化元模型API:

1、JAVA持久化中的动态元模型API

2、使用一个静态元模型


CaveatEmptor示例


CaveatEmptor是一个揭示ORM技术和Hibernate功能的在线拍卖应用,其中域模型为:




所对应的实体类为:




/model/src/main/java/org/jpwh/model/simple/Item.java

package org.jpwh.model.simple;

import javax.persistence.Entity;

import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Transient;
import javax.persistence.Version;
import javax.validation.constraints.Future;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

@Entity
public class Item {

    @Id
    @GeneratedValue(generator = "ID_GENERATOR")
    protected Long id;

    public Long getId() { // 可选但有用
        return id;
    }

    @Version
    protected long version;

    @NotNull
    @Size(
        min = 2,
        max = 255,
        message = "Name is required, maximum 255 characters."
    )
    protected String name;

    @Future
    protected Date auctionEnd;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getAuctionEnd() {
        return auctionEnd;
    }

    public void setAuctionEnd(Date auctionEnd) {
        this.auctionEnd = auctionEnd;
    }

    protected BigDecimal buyNowPrice;

    public BigDecimal getBuyNowPrice() {
        return buyNowPrice;
    }

    public void setBuyNowPrice(BigDecimal buyNowPrice) {
        this.buyNowPrice = buyNowPrice;
    }

    @Transient
    protected Set<Bid> bids = new HashSet<Bid>();

    public Set<Bid> getBids() {
        return bids;
    }

    public void setBids(Set<Bid> bids) {
        this.bids = bids;
    }

    @ManyToOne(fetch = FetchType.LAZY)
    protected Category category;

    public Category getCategory() {
        return category;
    }

    public void setCategory(Category category) {
        this.category = category;
    }

    public void addBid(Bid bid) {
        // Be defensive
        if (bid == null)
            throw new NullPointerException("Can't add null Bid");
        if (bid.getItem() != null)
            throw new IllegalStateException("Bid is already assigned to an Item");

        getBids().add(bid);
        bid.setItem(this);
    }

    public Bid placeBid(Bid currentHighestBid, BigDecimal bidAmount) {
        if (currentHighestBid == null ||
                bidAmount.compareTo(currentHighestBid.getAmount()) > 0) {
            return new Bid(bidAmount, this);
        }
        return null;
    }
}


/model/src/main/java/org/jpwh/model/simple/Item_.java

package org.jpwh.model.simple;

import java.util.Date;

import javax.persistence.metamodel.SingularAttribute;

@javax.persistence.metamodel.StaticMetamodel(Item.class)
public abstract class Item_ {

    public static volatile SingularAttribute<Item,Long> id;
	public static volatile SingularAttribute<Item,String> name;
	public static volatile SingularAttribute<Item,Date> auctionEnd;
}


/model/src/main/java/org/jpwh/model/simple/Bid.java

package org.jpwh.model.simple;

import org.jpwh.model.Constants;

import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;

@Entity
public class Bid {

    @Id
    @GeneratedValue(generator = Constants.ID_GENERATOR)
    protected Long id;

    public Long getId() { // 可选的但是有用的
        return id;
    }

    public Bid() {
    }

    @NotNull
    protected BigDecimal amount;

    public Bid(BigDecimal amount, Item item) {
        this.amount = amount;
        this.item = item;
    }

    public BigDecimal getAmount() {
        return amount;
    }

    public void setAmount(BigDecimal amount) {
        this.amount = amount;
    }

    @ManyToOne(optional = false, fetch = FetchType.LAZY) // NOT NULL
    @JoinColumn(name = "ITEM_ID") // 实际上默认名称
    protected Item item;

    public Bid(Item item) {
        this.item = item;
        item.getBids().add(this); // 双向
    }

    public Item getItem() {
        return item;
    }

    public void setItem(Item item) {
        this.item = item;
    }
}

/model/src/main/java/org/jpwh/model/simple/Address.java

package org.jpwh.model.simple;

import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.validation.constraints.NotNull;

/**
 * 
 *而不是<code> @Entity </code>,该组件POJO标有<code> @Embeddable </code>。 它
 *没有标识符属性。
 */
@Embeddable
public class Address {

    @NotNull // 忽略了DDL生成!
    @Column(nullable = false) // 用于DDL生成!
    protected String street;

    @NotNull
    @Column(nullable = false, length = 5) // Override VARCHAR(255)
    protected String zipcode;

    @NotNull
    @Column(nullable = false)
    protected String city;

    /**
     *Hibernate会调用这个无参数的构造函数来创建一个实例,然后
     *直接填充字段。
     */
    protected Address() {
    }

    /**
     *为方便起见,您可以拥有其他(public)构造函数。     
     **/
    public Address(String street, String zipcode, String city) {
        this.street = street;
        this.zipcode = zipcode;
        this.city = city;
    }

    public String getStreet() {
        return street;
    }

    public void setStreet(String street) {
        this.street = street;
    }

    public String getZipcode() {
        return zipcode;
    }

    public void setZipcode(String zipcode) {
        this.zipcode = zipcode;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }
}

/model/src/main/java/org/jpwh/model/simple/Category.java

package org.jpwh.model.simple;

import org.jpwh.model.Constants;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Category {

    @Id
    @GeneratedValue(generator = Constants.ID_GENERATOR)
    protected Long id;

    public Long getId() {
        return id;
    }

    protected String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

提供的一些测试类:




/examples/src/test/java/org/jpwh/test/simple/AccessJPAMetamodel.java

package org.jpwh.test.simple;

import org.jpwh.env.JPATest;
import org.jpwh.model.simple.Item;
import org.jpwh.model.simple.Item_;
import org.testng.annotations.Test;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Root;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.ManagedType;
import javax.persistence.metamodel.Metamodel;
import javax.persistence.metamodel.SingularAttribute;
import javax.persistence.metamodel.Type;
import javax.transaction.UserTransaction;
import java.util.Date;
import java.util.List;
import java.util.Set;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;

//JPA访问元模型
public class AccessJPAMetamodel extends JPATest {

    @Override
    public void configurePersistenceUnit() throws Exception {
        configurePersistenceUnit("SimpleXMLCompletePU");
    }

    @Test
    public void accessDynamicMetamodel() throws Exception {
        EntityManagerFactory entityManagerFactory = JPA.getEntityManagerFactory();

        Metamodel mm = entityManagerFactory.getMetamodel();

        Set<ManagedType<?>> managedTypes = mm.getManagedTypes();
        assertEquals(managedTypes.size(), 1);

        ManagedType itemType = managedTypes.iterator().next();
        assertEquals(
            itemType.getPersistenceType(),
            Type.PersistenceType.ENTITY
        );

        SingularAttribute nameAttribute =
            itemType.getSingularAttribute("name");
        assertEquals(
            nameAttribute.getJavaType(),
            String.class
        );
        assertEquals(
            nameAttribute.getPersistentAttributeType(),
            Attribute.PersistentAttributeType.BASIC
        );
        assertFalse(
            nameAttribute.isOptional() // NOT NULL
        );

        SingularAttribute auctionEndAttribute =
            itemType.getSingularAttribute("auctionEnd");
        assertEquals(
            auctionEndAttribute.getJavaType(),
            Date.class
        );
        assertFalse(
            auctionEndAttribute.isCollection()
        );
        assertFalse(
            auctionEndAttribute.isAssociation()
        );
    }

    @Test
    public void accessStaticMetamodel() throws Exception {

        SingularAttribute nameAttribute = Item_.name;

        assertEquals(
            nameAttribute.getJavaType(),
            String.class
        );
    }

    @Test
    public void queryStaticMetamodel() throws Exception {
        UserTransaction tx = TM.getUserTransaction();
        try {
            tx.begin();

            EntityManager entityManager = JPA.createEntityManager();

            Item itemOne = new Item();
            itemOne.setName("This is some item");
            itemOne.setAuctionEnd(new Date(System.currentTimeMillis() + 100000));
            entityManager.persist(itemOne);

            Item itemTwo = new Item();
            itemTwo.setName("Another item");
            itemTwo.setAuctionEnd(new Date(System.currentTimeMillis() + 100000));

            entityManager.persist(itemTwo);

            tx.commit();
            entityManager.close();

            entityManager = JPA.createEntityManager();
            tx.begin();

            CriteriaBuilder cb = entityManager.getCriteriaBuilder();

            // This query is the equivalent of "select i from Item i"
            CriteriaQuery<Item> query = cb.createQuery(Item.class);
            Root<Item> fromItem = query.from(Item.class);
            query.select(fromItem);

            List<Item> items =
                entityManager.createQuery(query)
                    .getResultList();

            assertEquals(items.size(), 2);

            // "where i.name like :pattern"
            Path<String> namePath = fromItem.get("name");
            query.where(
                cb.like(
                    namePath, // Has to be a Path<String> for like() operator!
                    cb.parameter(String.class, "pattern")
                )
            );

            items =
                entityManager.createQuery(query)
                    .setParameter("pattern", "%some item%") // Wildcards!
                    .getResultList();

            assertEquals(items.size(), 1);
            assertEquals(items.iterator().next().getName(), "This is some item");

            query.where(
                cb.like(
                    fromItem.get(Item_.name), // Static Item_ metamodel!
                    cb.parameter(String.class, "pattern")
                )
            );

            items =
                entityManager.createQuery(query)
                    .setParameter("pattern", "%some item%") // Wildcard!
                    .getResultList();

            assertEquals(items.size(), 1);
            assertEquals(items.iterator().next().getName(), "This is some item");

            tx.commit();
            entityManager.close();
        } finally {
            TM.rollback();
        }
    }

}


/examples/src/test/java/org/jpwh/test/simple/CRUD.java

package org.jpwh.test.simple;

import org.jpwh.env.JPATest;
import org.jpwh.model.simple.Item;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.transaction.UserTransaction;
import java.util.Date;
import java.util.List;

import static org.testng.Assert.assertEquals;

public class CRUD extends JPATest {

    @Override
    public void configurePersistenceUnit() throws Exception {
        configurePersistenceUnit("SimplePU");
    }

    @Test
    public void storeAndQueryItems() throws Exception {
        storeAndQueryItems("findItems");
    }

    public void storeAndQueryItems(String queryName) throws Exception {
        UserTransaction tx = TM.getUserTransaction();
        try {
            tx.begin();
            EntityManager em = JPA.createEntityManager();

            Item itemOne = new Item();
            itemOne.setName("Item One");
            itemOne.setAuctionEnd(new Date(System.currentTimeMillis() + 100000));
            em.persist(itemOne);

            Item itemTwo = new Item();
            itemTwo.setName("Item Two");
            itemTwo.setAuctionEnd(new Date(System.currentTimeMillis() + 100000));

            em.persist(itemTwo);

            tx.commit();
            em.close();

            tx.begin();
            em = JPA.createEntityManager();

            Query q = em.createNamedQuery(queryName);
            List<Item> items = q.getResultList();

            assertEquals(items.size(), 2);

            tx.commit();
            em.close();
        } finally {
            TM.rollback();
        }
    }

}

/examples/src/test/java/org/jpwh/test/simple/CRUDMetadataHBMXML.java

package org.jpwh.test.simple;

import org.testng.annotations.Test;

//CRUD HBM XML元数据
public class CRUDMetadataHBMXML extends CRUD {

    @Override
    public void configurePersistenceUnit() throws Exception {
        configurePersistenceUnit("SimpleXMLHibernatePU", "simple/Native.hbm.xml");
    }

    @Test
    @Override
    public void storeAndQueryItems() throws Exception {
        super.storeAndQueryItems("findItemsHibernate");
    }
}

/examples/src/test/java/org/jpwh/test/simple/CRUDMetadataOverrideXML.java

package org.jpwh.test.simple;

import org.hibernate.SessionFactory;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

//CRUD XML元数据覆盖
public class CRUDMetadataOverrideXML extends CRUD {

    @Override
    public void configurePersistenceUnit() throws Exception {
        configurePersistenceUnit("SimpleXMLOverridePU");
    }

    @Test
    @Override
    public void storeAndQueryItems() throws Exception {
        super.storeAndQueryItems();
    }

    @Test
    public void checkMetadataOverride() throws Exception {
    	
    	//使用Hibernate元数据API来查找覆盖SQL列的名称,JPA不
	    //支持访问SQL详细信息
	    // TODO:不知道如何使用Hibernate 5 API访问SQL映射详细信息...
    	
        /*
        Property nameProperty = itemClass.getProperty("name");
        Column nameColumn = (Column) nameProperty.getColumnIterator().next();

        // 列的名称来自XML描述符
        assertEquals(nameColumn.getName(), "ITEM_NAME");

        // 但是Bean验证注释仍然被识别!
        assertEquals(nameColumn.isNullable(), false);
        assertEquals(nameColumn.getLength(), 255);
        */
    }
}

/examples/src/test/java/org/jpwh/test/simple/CRUDMetadataXML.java

package org.jpwh.test.simple;

import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

//CRUD XML元数据
public class CRUDMetadataXML extends CRUD {

    @Override
    public void configurePersistenceUnit() throws Exception {
        configurePersistenceUnit("SimpleXMLCompletePU");
    }

    @Test
    @Override
    public void storeAndQueryItems() throws Exception {
        super.storeAndQueryItems();
    }
}

/examples/src/test/java/org/jpwh/test/simple/MappingEmbeddables.java

package org.jpwh.test.simple;

import org.jpwh.env.JPATest;
import org.jpwh.model.simple.Address;
import org.jpwh.model.simple.User;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceException;
import javax.transaction.UserTransaction;

import java.util.logging.Level;
import java.util.logging.Logger;

import static org.testng.Assert.assertEquals;


//映射可嵌入
public class MappingEmbeddables extends JPATest {

    private static final Logger LOG = Logger.getLogger(MappingEmbeddables.class.getName());

    @Override
    public void configurePersistenceUnit() throws Exception {
        configurePersistenceUnit("SimplePU");
    }

    @Test
    public void storeAndLoadUsers() throws Exception {
        UserTransaction tx = TM.getUserTransaction();
        try {
            tx.begin();
            EntityManager em = JPA.createEntityManager();

            User user = new User();
            user.setUsername("johndoe");
            Address homeAddress = new Address("Some Street 123", "12345", "Some City");
            user.setHomeAddress(homeAddress);
            em.persist(user);

            tx.commit();
            em.close();

            tx.begin();
            em = JPA.createEntityManager();

            User u = em.find(User.class, user.getId());

            assertEquals(u.getUsername(), "johndoe");
            assertEquals(u.getHomeAddress().getStreet(), "Some Street 123");

            tx.commit();
            em.close();
        } finally {
            TM.rollback();
        }
    }

    @Test(expectedExceptions = org.hibernate.exception.ConstraintViolationException.class)
    public void storeAndLoadInvalidUsers() throws Throwable {
        UserTransaction tx = TM.getUserTransaction();
        try {
            tx.begin();
            EntityManager em = JPA.createEntityManager();

            User user = new User();
            user.setUsername("johndoe");
            Address homeAddress = new Address("Some Street 123", "12345", null); // NULL city!
            user.setHomeAddress(homeAddress);
            em.persist(user);

            try {
                // Hibernate尝试插入但失败
                em.flush();

              //注意:如果你尝试用tx.commit()和一个冲洗的副作用,你就不会
              //得到ConstraintViolationException。Hibernate会在内部捕获它
              //简单地将事务标记为回滚。

            } catch (Exception ex) {
                throw unwrapCauseOfType(ex, org.hibernate.exception.ConstraintViolationException.class);
            }
        } finally {
            TM.rollback();
        }
    }

}

/examples/src/test/java/org/jpwh/test/simple/MappingManyToOne.java

package org.jpwh.test.simple;

import org.jpwh.env.JPATest;
import org.jpwh.model.simple.Bid;
import org.jpwh.model.simple.Item;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

import javax.persistence.EntityManager;
import javax.transaction.UserTransaction;
import java.math.BigDecimal;

import static org.testng.Assert.assertEquals;

//映射多对一
public class MappingManyToOne extends JPATest {

    @Override
    public void configurePersistenceUnit() throws Exception {
        configurePersistenceUnit("SimplePU");
    }

    @Test
    public void storeAndLoadBids() throws Exception {
        UserTransaction tx = TM.getUserTransaction();
        try {
            tx.begin();
            EntityManager em = JPA.createEntityManager();

            //存储在一个持久性上下文(事务)
            Item anItem = new Item();
            anItem.setName("Example Item");

            Bid firstBid = new Bid(new BigDecimal("123.00"), anItem);

            Bid secondBid = new Bid(new BigDecimal("456.00"), anItem);

            // 订单在这里很重要,Hibernate不够聪明了!
            em.persist(anItem);
            em.persist(firstBid);
            em.persist(secondBid);

            tx.commit();
            em.close();

            tx.begin();
            em = JPA.createEntityManager();

            Long BID_ID = firstBid.getId();

            // 加载另一个持久性上下文
            Bid someBid = em.find(Bid.class, BID_ID); // SQL SELECT

          //初始化Item代理,因为我们调用getId(),它是
          //未映射为标识符属性(the field is!)
            assertEquals(someBid.getItem().getId(), anItem.getId()); // SQL SELECT

            tx.commit();
            em.close();
        } finally {
            TM.rollback();
        }
    }

}

/examples/src/test/java/org/jpwh/test/simple/ModelOperations.java

package org.jpwh.test.simple;

import org.jpwh.model.simple.Bid;
import org.jpwh.model.simple.Item;
import org.testng.annotations.Test;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import java.util.Date;
import java.util.Locale;
import java.util.Set;

import static org.testng.Assert.*;

//模式操作
public class ModelOperations {

    @Test
    public void linkBidAndItem() {
        Item anItem = new Item();
        Bid aBid = new Bid();

        anItem.getBids().add(aBid);
        aBid.setItem(anItem);

        assertEquals(anItem.getBids().size(), 1);
        assertTrue(anItem.getBids().contains(aBid));
        assertEquals(aBid.getItem(), anItem);

        // 再次用方便的方法
        Bid secondBid = new Bid();
        anItem.addBid(secondBid);

        assertEquals(2, anItem.getBids().size());
        assertTrue(anItem.getBids().contains(secondBid));
        assertEquals(anItem, secondBid.getItem());
    }

    @Test
    public void validateItem() {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        Validator validator = factory.getValidator();

        Item item = new Item();
        item.setName("Some Item");
        item.setAuctionEnd(new Date());

        Set<ConstraintViolation<Item>> violations = validator.validate(item);

        // 我们有一个验证错误,拍卖结束日期不在将来!
        assertEquals(1, violations.size());

        ConstraintViolation<Item> violation = violations.iterator().next();
        String failedPropertyName =
                violation.getPropertyPath().iterator().next().getName();

        assertEquals(failedPropertyName, "auctionEnd");

        if (Locale.getDefault().getLanguage().equals("en"))
            assertEquals(violation.getMessage(), "must be in the future");
    }

}

/examples/src/test/java/org/jpwh/test/simple/SimpleTransitions.java

package org.jpwh.test.simple;

import org.hibernate.Session;
import org.hibernate.jdbc.Work;
import org.jpwh.env.JPATest;
import org.jpwh.model.simple.Address;
import org.jpwh.model.simple.Item;
import org.jpwh.model.simple.User;
import org.testng.annotations.Test;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.FlushModeType;
import javax.persistence.PersistenceUnitUtil;
import javax.transaction.Status;
import javax.transaction.UserTransaction;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;

import static org.testng.Assert.*;
import static org.testng.Assert.assertEquals;

//简单的过渡
public class SimpleTransitions extends JPATest {

    @Override
    public void configurePersistenceUnit() throws Exception {
        configurePersistenceUnit("SimplePU");
    }

    @Test
    public void basicUOW() {
        EntityManager em = null;
        UserTransaction tx = TM.getUserTransaction();
        try {
            tx.begin();
            em = JPA.createEntityManager(); // 应用程序管理

            Item someItem = new Item();
            someItem.setName("Some Item");
            em.persist(someItem);

            tx.commit(); // 同步/刷新持久性上下文
        } catch (Exception ex) {
            // 事务回滚,异常处理
            try {
                if (tx.getStatus() == Status.STATUS_ACTIVE
                    || tx.getStatus() == Status.STATUS_MARKED_ROLLBACK)
                    tx.rollback();
            } catch (Exception rbEx) {
                System.err.println("Rollback of transaction failed, trace follows!");
                rbEx.printStackTrace(System.err);
            }
            throw new RuntimeException(ex);
        } finally {
            if (em != null && em.isOpen())
                em.close(); // 你创建它,你关闭它!
        }
    }

    @Test
    public void makePersistent() throws Exception {
        UserTransaction tx = TM.getUserTransaction();
        try {
            EntityManager em;

            tx.begin();
            em = JPA.createEntityManager();
            Item item = new Item();
            item.setName("Some Item"); // Item#name is NOT NULL!

            em.persist(item);

            Long ITEM_ID = item.getId(); // Has been assigned

            tx.commit();
            em.close();

            tx.begin();
            em = JPA.createEntityManager();
            assertEquals(em.find(Item.class, ITEM_ID).getName(), "Some Item");
            tx.commit();
            em.close();

        } finally {
            TM.rollback();
        }
    }

    @Test
    public void retrievePersistent() throws Exception {
        UserTransaction tx = TM.getUserTransaction();
        try {
            tx.begin();
            EntityManager em = JPA.createEntityManager();
            Item someItem = new Item();
            someItem.setName("Some Item");
            em.persist(someItem);
            tx.commit();
            em.close();
            long ITEM_ID = someItem.getId();

            {
                tx.begin();
                em = JPA.createEntityManager();

                // Hit the database if not already in persistence context
                Item item = em.find(Item.class, ITEM_ID);

                if (item != null)
                    item.setName("New Name"); // Modify

                tx.commit(); // Flush: Dirty check and SQL UPDATE
                em.close();
            }

            {
                tx.begin();
                em = JPA.createEntityManager();

                Item itemA = em.find(Item.class, ITEM_ID);
                Item itemB = em.find(Item.class, ITEM_ID); // Repeatable read

                assertTrue(itemA == itemB);
                assertTrue(itemA.equals(itemB));
                assertTrue(itemA.getId().equals(itemB.getId()));

                tx.commit(); // Flush: Dirty check and SQL UPDATE
                em.close();
            }

            tx.begin();
            em = JPA.createEntityManager();
            assertEquals(em.find(Item.class, ITEM_ID).getName(), "New Name");
            tx.commit();
            em.close();
        } finally {
            TM.rollback();
        }
    }

    @Test(expectedExceptions = org.hibernate.LazyInitializationException.class)
    public void retrievePersistentReference() throws Exception {
        UserTransaction tx = TM.getUserTransaction();
        try {
            tx.begin();
            EntityManager em = JPA.createEntityManager();
            Item someItem = new Item();
            someItem.setName("Some Item");
            em.persist(someItem);
            tx.commit();
            em.close();
            long ITEM_ID = someItem.getId();

            tx.begin();
            em = JPA.createEntityManager();

            /* 
				如果持久性上下文已经包含具有给定标识符的<code> Item </code>
                <code> Item </code>实例由<code> getReference()</code>返回,而不会触发数据库。
                此外,如果目前正在管理具有该标识符的持久化实例<em>no</em>,那么一个空
                占位符将由Hibernate代理生成。 这意味着<code> getReference()</code>不会
                访问数据库,它不返回<code> null </code>,与<code> find()</code>不同。
             */
            Item item = em.getReference(Item.class, ITEM_ID);

            /* 
				JPA提供<code> PersistenceUnitUtil </code>辅助方法,例如<code> isLoaded()</code>
                检测您是否使用未初始化的代理。
            */
            PersistenceUnitUtil persistenceUtil =
                JPA.getEntityManagerFactory().getPersistenceUnitUtil();
            assertFalse(persistenceUtil.isLoaded(item));

            /* 
				一旦您在代理上调用任何方法,例如<code> Item#getName()</code>,
                执行<code> SELECT </code>以完全初始化占位符。 这个规则的例外是
                一种映射的数据库标识符getter方法,如<code> getId()</code>。 代理人
                可能看起来像真实的东西,但它只是一个携带标识符值的占位符
                它代表的实体实例。 如果代理服务器上不存在数据库记录
                被初始化,将抛出一个<code> EntityNotFoundException </code>。
             */
            // assertEquals(item.getName(), "Some Item");
            /* 
               Hibernate有一个方便的静态<code> initialize()</code>方法,加载代理的数据。
             */
            // Hibernate.initialize(item);

            tx.commit();
            em.close();

            /* 
			在持久化上下文关闭之后,<code>item</code>处于分离状态。
			如果你这样做在持久化上下文仍然打开时,不初始化代理,如果你访问代理,您将得到一个
			<code> LazyInitializationException </code>。
			你不能加载,一旦持久化上下文关闭,就按需数据。
			解决方案很简单:加载
			关闭持久化上下文之前的数据。
             */
            assertEquals(item.getName(), "Some Item");
        } finally {
            TM.rollback();
        }
    }

    @Test
    public void makeTransient() throws Exception {
        UserTransaction tx = TM.getUserTransaction();
        try {
            tx.begin();
            EntityManager em = JPA.createEntityManager();
            Item someItem = new Item();
            someItem.setName("Some Item");
            em.persist(someItem);
            tx.commit();
            em.close();
            long ITEM_ID = someItem.getId();

            tx.begin();
            em = JPA.createEntityManager();
            /* 
				如果调用<code>find()</code>,Hibernate将执行<code>SELECT</code>
                加载<code>Item</code>。 如果你调用<code> getReference()</code>,Hibernate
                将尝试避免<code>SELECT</code>并返回代理。
             */
            Item item = em.find(Item.class, ITEM_ID);
            //Item item = em.getReference(Item.class, ITEM_ID);

            /* 
			       调用<code>remove()</code>将队列化实体删除
                工作单位完成,现在已被删除<em>removed</em>。如果<code>remove()</code>
                在代理上调用,Hibernate将执行<code>SELECT</code>来加载数据。
                实体实例必须在生命周期过渡期间完全初始化。 你可能
				是否启用了生命周期回调方法或实体侦听器
                (请参阅<a href="#EventListenersInterceptors"/>),实例必须通过这些
                拦截器完成其整个生命周期。
             */
            em.remove(item);

            /* 
				 被删除状态的实体不再处于持久状态,这可以是
                 使用<code> contains()</code>操作进行检查。
             */
            assertFalse(em.contains(item));

            /* 
               	您可以使删除的实例再次持久化,取消删除。
             */
            // em.persist(item);

            // hibernate.use_identifier_rollback已启用,它现在看起来像一个暂时的实例
            assertNull(item.getId());

            /* 
				当事务提交时,Hibernate会同步状态转换
                数据库并执行SQL <code> DELETE </code>。 JVM垃圾收集器检测到
                <code> item </code>不再被任何人引用,最后删除最后一个跟踪
                数据。
             */
            tx.commit();
            em.close();

            tx.begin();
            em = JPA.createEntityManager();
            item = em.find(Item.class, ITEM_ID);
            assertNull(item);
            tx.commit();
            em.close();
        } finally {
            TM.rollback();
        }
    }

    @Test
    public void refresh() throws Exception {
        UserTransaction tx = TM.getUserTransaction();
        try {
            tx.begin();
            EntityManager em = JPA.createEntityManager();
            Item someItem = new Item();
            someItem.setName("Some Item");
            em.persist(someItem);
            tx.commit();
            em.close();
            final long ITEM_ID = someItem.getId();

            tx.begin();
            em = JPA.createEntityManager();

            Item item = em.find(Item.class, ITEM_ID);
            item.setName("Some Name");

            // 有人更新数据库中的这一行!
            Executors.newSingleThreadExecutor().submit(new Callable<Object>() {
                @Override
                public Object call() throws Exception {
                    UserTransaction tx = TM.getUserTransaction();
                    try {
                        tx.begin();
                        EntityManager em = JPA.createEntityManager();

                        Session session = em.unwrap(Session.class);
                        session.doWork(new Work() {
                            @Override
                            public void execute(Connection con) throws SQLException {
                                PreparedStatement ps = con.prepareStatement("update ITEM set name = ? where ID = ?");
                                ps.setString(1, "Concurrent Update Name");
                                ps.setLong(2, ITEM_ID);

                                /* 替代方法:在刷新时收到EntityNotFoundException
                                PreparedStatement ps = con.prepareStatement("delete from ITEM where ID = ?");
                                ps.setLong(1, ITEM_ID);
                                */

                                if (ps.executeUpdate() != 1)
                                    throw new SQLException("ITEM row was not updated");
                            }
                        });

                        tx.commit();
                        em.close();

                    } catch (Exception ex) {
                        TM.rollback();
                        throw new RuntimeException("Concurrent operation failure: " + ex, ex);
                    }
                    return null;
                }
            }).get();

            String oldName = item.getName();
            em.refresh(item);
            assertNotEquals(item.getName(), oldName);
            assertEquals(item.getName(), "Concurrent Update Name");

            tx.commit(); // 刷新:脏检查和SQL UPDATE
            em.close();

        } finally {
            TM.rollback();
        }
    }

    @Test(groups = {"H2", "POSTGRESQL", "ORACLE"})
    public void replicate() throws Exception {

        Long ITEM_ID;
        try {
            UserTransaction tx = TM.getUserTransaction();
            tx.begin();
            EntityManager em = JPA.createEntityManager();
            Item someItem = new Item();
            someItem.setName("Some Item");
            em.persist(someItem);
            tx.commit();
            em.close();
            ITEM_ID = someItem.getId();
        } finally {
            TM.rollback();
        }

        UserTransaction tx = TM.getUserTransaction();
        try {
            tx.begin();

            EntityManager emA = getDatabaseA().createEntityManager();
            Item item = emA.find(Item.class, ITEM_ID);

            EntityManager emB = getDatabaseB().createEntityManager();
            emB.unwrap(Session.class)
                .replicate(item, org.hibernate.ReplicationMode.LATEST_VERSION);

            tx.commit();
            emA.close();
            emB.close();
        } finally {
            TM.rollback();
        }
    }

    protected EntityManagerFactory getDatabaseA() {
        return JPA.getEntityManagerFactory();
    }

    protected EntityManagerFactory getDatabaseB() {
		 // TODO:失败,因为我们不能在同一事务中招募两个非XA连接
    	 //在MySQL上 XA在MySQL中破坏,所以我们必须使用Bitronix XA包装器,它可以
         //每个事务只处理一个非XA资源。 请参阅DatabaseProduct.java
        return JPA.getEntityManagerFactory();
    }

    @Test
    public void flushModeType() throws Exception {
        UserTransaction tx = TM.getUserTransaction();
        Long ITEM_ID;
        try {
            tx.begin();
            EntityManager em = JPA.createEntityManager();
            Item someItem = new Item();
            someItem.setName("Original Name");
            em.persist(someItem);
            tx.commit();
            em.close();
            ITEM_ID = someItem.getId();
        } finally {
            TM.rollback();
        }

        try {
            tx.begin();
            EntityManager em = JPA.createEntityManager();

            Item item = em.find(Item.class, ITEM_ID);
            item.setName("New Name");

            // 禁用刷新之前查询:
            em.setFlushMode(FlushModeType.COMMIT);

            assertEquals(
                em.createQuery("select i.name from Item i where i.id = :id")
                    .setParameter("id", ITEM_ID).getSingleResult(),
                "Original Name"
            );

            tx.commit(); // Flush!
            em.close();
        } finally {
            TM.rollback();
        }
    }

    @Test
    public void scopeOfIdentity() throws Exception {
        UserTransaction tx = TM.getUserTransaction();
        try {
            tx.begin();
            EntityManager em = JPA.createEntityManager();
            Item someItem = new Item();
            someItem.setName("Some Item");
            em.persist(someItem);
            tx.commit();
            em.close();
            long ITEM_ID = someItem.getId();

            tx.begin();
            em = JPA.createEntityManager();

            Item a = em.find(Item.class, ITEM_ID);
            Item b = em.find(Item.class, ITEM_ID);
            assertTrue(a == b);
            assertTrue(a.equals(b));
            assertEquals(a.getId(), b.getId());

            tx.commit();
            em.close();
            // PC is gone, 'a' and 'b' are now references to instances in detached state!

            tx.begin();
            em = JPA.createEntityManager();

            Item c = em.find(Item.class, ITEM_ID);
            assertTrue(a != c); // The 'a' reference is still detached!
            assertFalse(a.equals(c));
            assertEquals(a.getId(), c.getId());

            tx.commit();
            em.close();

            Set<Item> allItems = new HashSet<>();
            allItems.add(a);
            allItems.add(b);
            allItems.add(c);
            assertEquals(allItems.size(), 2); // 这似乎是错的和任意的!

        } finally {
            TM.rollback();
        }
    }

    @Test
    public void detach() throws Exception {
        UserTransaction tx = TM.getUserTransaction();
        try {
            tx.begin();
            EntityManager em = JPA.createEntityManager();

            User someUser = new User();
            someUser.setUsername("johndoe");
            someUser.setHomeAddress(new Address("Some Street", "1234", "Some City"));
            em.persist(someUser);
            tx.commit();
            em.close();
            long USER_ID = someUser.getId();

            tx.begin();
            em = JPA.createEntityManager();

            User user = em.find(User.class, USER_ID);

            em.detach(user);

            assertFalse(em.contains(user));

            tx.commit();
            em.close();
        } finally {
            TM.rollback();
        }
    }

    @Test
    public void mergeDetached() throws Exception {
        UserTransaction tx = TM.getUserTransaction();
        try {
            tx.begin();
            EntityManager em = JPA.createEntityManager();

            User detachedUser = new User();
            detachedUser.setUsername("foo");
            detachedUser.setHomeAddress(new Address("Some Street", "1234", "Some City"));
            em.persist(detachedUser);
            tx.commit();
            em.close();
            long USER_ID = detachedUser.getId();

            detachedUser.setUsername("johndoe");

            tx.begin();
            em = JPA.createEntityManager();

            User mergedUser = em.merge(detachedUser);
            // 合并后丢弃'detachUser'引用!

            // 'mergedUser'处于持久状态
            mergedUser.setUsername("doejohn");

            tx.commit(); // UPDATE在数据库中
            em.close();

            tx.begin();
            em = JPA.createEntityManager();
            User user = em.find(User.class, USER_ID);
            assertEquals(user.getUsername(), "doejohn");
            tx.commit();
            em.close();
        } finally {
            TM.rollback();
        }
    }

}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值