一、结构
二、配置文件约定
The JPA provider automatically picks up this descriptor if you place it in a META-INF /orm.xml file on the classpath of the persistence unit. If you prefer to use a different name or several files, you’ll have to change the configuration of the persistence unit in your META-INF /persistence.xml file:
If you include the <xml-mapping-metadata-complete> element, the JPA provider ignores all annotations on your domain model classes in this persistence unit and relies only on the mappings as defined in the XML descriptor(s). You can (redundantly in this case) enable this on an entity level, with <metadata-complete="true"/> . If enabled, the JPA provider assumes that you mapped all attributes of the entity in XML and that it should ignore all annotations for this particular entity.
1.Mappings.xml
1 <entity-mappings 2 version="2.1" 3 xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm" 4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence/orm 6 http://xmlns.jcp.org/xml/ns/persistence/orm_2_1.xsd"> 7 8 <!-- First, global metadata --> 9 <persistence-unit-metadata> 10 11 <!-- Ignore all annotations, all mapping metadata in XML files --> 12 <xml-mapping-metadata-complete/> 13 14 <!-- Some default settings --> 15 <persistence-unit-defaults> 16 <!-- 17 Escape all SQL column/table/etc. names, e.g. if your SQL 18 names are actually keywords (a "USER" table for example) 19 --> 20 <delimited-identifiers/> 21 </persistence-unit-defaults> 22 23 </persistence-unit-metadata> 24 25 <entity class="org.jpwh.model.simple.Item" access="FIELD"> 26 <attributes> 27 <id name="id"> 28 <generated-value strategy="AUTO"/> 29 </id> 30 <basic name="name"/> 31 <basic name="auctionEnd"> 32 <temporal>TIMESTAMP</temporal> 33 </basic> 34 <transient name="bids"/> 35 <transient name="category"/> 36 </attributes> 37 </entity> 38 39 </entity-mappings>
2.MappingsOverride.xml
1 <entity-mappings 2 version="2.1" 3 xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm" 4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence/orm 6 http://xmlns.jcp.org/xml/ns/persistence/orm_2_1.xsd"> 7 8 <entity class="org.jpwh.model.simple.Item"> 9 <attributes> 10 <!-- Override the SQL column name --> 11 <basic name="name"> 12 <column name="ITEM_NAME"/> 13 </basic> 14 </attributes> 15 </entity> 16 17 </entity-mappings>
3.Queries.xml
1 <entity-mappings 2 version="2.1" 3 xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm" 4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence/orm 6 http://xmlns.jcp.org/xml/ns/persistence/orm_2_1.xsd"> 7 8 <named-query name="findItems"> 9 <query><![CDATA[ 10 select i from Item i 11 ]]></query> 12 </named-query> 13 14 <named-query name="findItemsWithHints"> 15 <query>select i from Item i</query> 16 <hint name="org.hibernate.comment" value="My Comment"/> 17 <hint name="org.hibernate.fetchSize" value="50"/> 18 <hint name="org.hibernate.readOnly" value="true"/> 19 <hint name="org.hibernate.timeout" value="60"/> 20 </named-query> 21 22 </entity-mappings>
4.Native.hbm.xml
1 <?xml version="1.0"?> 2 <!-- 3 Metadata is declared inside a <code><hibernate-mapping></code> root element. Attributes such as 4 <code>package</code> name and <code>default-access</code> apply to all mappings in this file. You may include as many 5 entity class mappings as you like. 6 --> 7 <hibernate-mapping 8 xmlns="http://www.hibernate.org/xsd/orm/hbm" 9 package="org.jpwh.model.simple" 10 default-access="field"> 11 12 <!-- An entity class mapping --> 13 <class name="Item"> 14 <id name="id"> 15 <generator class="native"/> 16 </id> 17 <property name="name"/> 18 <property name="auctionEnd" type="timestamp"/> 19 </class> 20 21 <!-- Externalized queries --> 22 <query name="findItemsHibernate">select i from Item i</query> 23 24 <!-- Auxiliary schema DDL --> 25 <database-object> 26 <create>create index ITEM_NAME_IDX on ITEM(NAME)</create> 27 <drop>drop index if exists ITEM_NAME_IDX</drop> 28 </database-object> 29 30 </hibernate-mapping>
三、domain层
1.
1 package org.jpwh.model.querying; 2 3 import org.jpwh.model.Constants; 4 5 import javax.persistence.*; 6 import javax.validation.constraints.NotNull; 7 import java.math.BigDecimal; 8 9 @Entity 10 public class Bid { 11 12 @Id 13 @GeneratedValue(generator = Constants.ID_GENERATOR) 14 protected Long id; 15 16 @NotNull 17 @ManyToOne(fetch = FetchType.LAZY) 18 @JoinColumn(foreignKey = @ForeignKey(name = "FK_BID_ITEM_ID")) 19 protected Item item; 20 21 @ManyToOne(optional = false, fetch = FetchType.LAZY) 22 @JoinColumn(foreignKey = @ForeignKey(name = "FK_BID_BIDDER_ID")) 23 protected User bidder; 24 25 @NotNull 26 protected BigDecimal amount; 27 28 public Bid() { 29 } 30 31 public Bid(Item item, User bidder, BigDecimal amount) { 32 this.item = item; 33 this.amount = amount; 34 this.bidder = bidder; 35 } 36 37 public Item getItem() { 38 return item; 39 } 40 41 public void setItem(Item item) { 42 this.item = item; 43 } 44 45 public User getBidder() { 46 return bidder; 47 } 48 49 public void setBidder(User bidder) { 50 this.bidder = bidder; 51 } 52 53 public BigDecimal getAmount() { 54 return amount; 55 } 56 57 public void setAmount(BigDecimal amount) { 58 this.amount = amount; 59 } 60 }
2.
1 package org.jpwh.model.querying; 2 3 import org.jpwh.model.Constants; 4 5 import javax.persistence.*; 6 import javax.validation.constraints.NotNull; 7 import java.math.BigDecimal; 8 import java.util.Date; 9 import java.util.HashSet; 10 import java.util.Set; 11 12 @NamedQueries({ 13 @NamedQuery( 14 name = "findItemById", 15 query = "select i from Item i where i.id = :id" 16 ) 17 , 18 @NamedQuery( 19 name = "findItemByName", 20 query = "select i from Item i where i.name like :name", 21 hints = { 22 @QueryHint( 23 name = org.hibernate.annotations.QueryHints.TIMEOUT_JPA, 24 value = "60000"), 25 @QueryHint( 26 name = org.hibernate.annotations.QueryHints.COMMENT, 27 value = "Custom SQL comment") 28 } 29 ) 30 }) 31 @SqlResultSetMappings({ 32 @SqlResultSetMapping( 33 name = "ItemResult", 34 entities = 35 @EntityResult( 36 entityClass = Item.class, 37 fields = { 38 @FieldResult(name = "id", column = "ID"), 39 @FieldResult(name = "name", column = "EXTENDED_NAME"), 40 @FieldResult(name = "createdOn", column = "CREATEDON"), 41 @FieldResult(name = "auctionEnd", column = "AUCTIONEND"), 42 @FieldResult(name = "auctionType", column = "AUCTIONTYPE"), 43 @FieldResult(name = "approved", column = "APPROVED"), 44 @FieldResult(name = "buyNowPrice", column = "BUYNOWPRICE"), 45 @FieldResult(name = "seller", column = "SELLER_ID") 46 } 47 ) 48 ) 49 }) 50 @Entity 51 public class Item { 52 53 @Id 54 @GeneratedValue(generator = Constants.ID_GENERATOR) 55 protected Long id; 56 57 @NotNull 58 protected String name; 59 60 @NotNull 61 protected Date createdOn = new Date(); 62 63 @NotNull 64 protected Date auctionEnd; 65 66 @NotNull 67 @Enumerated(EnumType.STRING) 68 protected AuctionType auctionType = AuctionType.HIGHEST_BID; 69 70 @NotNull 71 protected boolean approved = true; 72 73 protected BigDecimal buyNowPrice; 74 75 @ManyToOne(optional = false, fetch = FetchType.LAZY) 76 @JoinColumn(foreignKey = @ForeignKey(name = "FK_ITEM_SELLER_ID")) 77 protected User seller; 78 79 @ManyToMany(mappedBy = "items") 80 protected Set<Category> categories = new HashSet<>(); 81 82 @OneToMany(mappedBy = "item") 83 protected Set<Bid> bids = new HashSet<>(); 84 85 @ElementCollection 86 @JoinColumn(foreignKey = @ForeignKey(name = "FK_ITEM_IMAGES_ITEM_ID")) 87 protected Set<Image> images = new HashSet<>(); 88 89 public Item() { 90 } 91 92 public Item(String name, Date auctionEnd, User seller) { 93 this.name = name; 94 this.auctionEnd = auctionEnd; 95 this.seller = seller; 96 } 97 98 public Long getId() { 99 return id; 100 } 101 102 public String getName() { 103 return name; 104 } 105 106 public void setName(String name) { 107 this.name = name; 108 } 109 110 public Date getCreatedOn() { 111 return createdOn; 112 } 113 114 public Date getAuctionEnd() { 115 return auctionEnd; 116 } 117 118 public void setAuctionEnd(Date auctionEnd) { 119 this.auctionEnd = auctionEnd; 120 } 121 122 public AuctionType getAuctionType() { 123 return auctionType; 124 } 125 126 public void setAuctionType(AuctionType auctionType) { 127 this.auctionType = auctionType; 128 } 129 130 public boolean isApproved() { 131 return approved; 132 } 133 134 public void setApproved(boolean approved) { 135 this.approved = approved; 136 } 137 138 public BigDecimal getBuyNowPrice() { 139 return buyNowPrice; 140 } 141 142 public void setBuyNowPrice(BigDecimal buyNowPrice) { 143 this.buyNowPrice = buyNowPrice; 144 } 145 146 public User getSeller() { 147 return seller; 148 } 149 150 public void setSeller(User seller) { 151 this.seller = seller; 152 } 153 154 public Set<Category> getCategories() { 155 return categories; 156 } 157 158 public void setCategories(Set<Category> categories) { 159 this.categories = categories; 160 } 161 162 public Set<Bid> getBids() { 163 return bids; 164 } 165 166 public void setBids(Set<Bid> bids) { 167 this.bids = bids; 168 } 169 170 public Set<Image> getImages() { 171 return images; 172 } 173 174 public void setImages(Set<Image> images) { 175 this.images = images; 176 } 177 // ... 178 }
3.
1 @org.hibernate.annotations.NamedQueries({ 2 @org.hibernate.annotations.NamedQuery( 3 name = "findItemsOrderByName", 4 query = "select i from Item i order by i.name asc" 5 ) 6 , 7 @org.hibernate.annotations.NamedQuery( 8 name = "findItemBuyNowPriceGreaterThan", 9 query = "select i from Item i where i.buyNowPrice > :price", 10 timeout = 60, // Seconds! 11 comment = "Custom SQL comment" 12 ) 13 }) 14 15 package org.jpwh.model.querying;
四、测试文件
1.
1 package org.jpwh.test.simple; 2 3 import org.jpwh.model.simple.Bid; 4 import org.jpwh.model.simple.Item; 5 import org.testng.annotations.Test; 6 7 import javax.validation.ConstraintViolation; 8 import javax.validation.Validation; 9 import javax.validation.Validator; 10 import javax.validation.ValidatorFactory; 11 import java.util.Date; 12 import java.util.Locale; 13 import java.util.Set; 14 15 import static org.testng.Assert.*; 16 17 public class ModelOperations { 18 19 @Test 20 public void linkBidAndItem() { 21 Item anItem = new Item(); 22 Bid aBid = new Bid(); 23 24 anItem.getBids().add(aBid); 25 aBid.setItem(anItem); 26 27 assertEquals(anItem.getBids().size(), 1); 28 assertTrue(anItem.getBids().contains(aBid)); 29 assertEquals(aBid.getItem(), anItem); 30 31 // Again with convenience method 32 Bid secondBid = new Bid(); 33 anItem.addBid(secondBid); 34 35 assertEquals(2, anItem.getBids().size()); 36 assertTrue(anItem.getBids().contains(secondBid)); 37 assertEquals(anItem, secondBid.getItem()); 38 } 39 40 @Test 41 public void validateItem() { 42 ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); 43 Validator validator = factory.getValidator(); 44 45 Item item = new Item(); 46 item.setName("Some Item"); 47 item.setAuctionEnd(new Date()); 48 49 Set<ConstraintViolation<Item>> violations = validator.validate(item); 50 51 // We have one validation error, auction end date was not in the future! 52 assertEquals(1, violations.size()); 53 54 ConstraintViolation<Item> violation = violations.iterator().next(); 55 String failedPropertyName = 56 violation.getPropertyPath().iterator().next().getName(); 57 58 assertEquals(failedPropertyName, "auctionEnd"); 59 60 if (Locale.getDefault().getLanguage().equals("en")) 61 assertEquals(violation.getMessage(), "must be in the future"); 62 } 63 64 }
2.
1 package org.jpwh.test.simple; 2 3 import org.jpwh.env.JPATest; 4 import org.jpwh.model.simple.Item; 5 //import org.jpwh.model.simple.Item_; 6 import org.testng.annotations.Test; 7 8 import javax.persistence.EntityManager; 9 import javax.persistence.EntityManagerFactory; 10 import javax.persistence.criteria.CriteriaBuilder; 11 import javax.persistence.criteria.CriteriaQuery; 12 import javax.persistence.criteria.Path; 13 import javax.persistence.criteria.Root; 14 import javax.persistence.metamodel.Attribute; 15 import javax.persistence.metamodel.ManagedType; 16 import javax.persistence.metamodel.Metamodel; 17 import javax.persistence.metamodel.SingularAttribute; 18 import javax.persistence.metamodel.Type; 19 import javax.transaction.UserTransaction; 20 import java.util.Date; 21 import java.util.List; 22 import java.util.Set; 23 24 import static org.testng.Assert.assertEquals; 25 import static org.testng.Assert.assertFalse; 26 27 public class AccessJPAMetamodel extends JPATest { 28 29 @Override 30 public void configurePersistenceUnit() throws Exception { 31 configurePersistenceUnit("SimpleXMLCompletePU"); 32 } 33 34 @Test 35 public void accessDynamicMetamodel() throws Exception { 36 EntityManagerFactory entityManagerFactory = JPA.getEntityManagerFactory(); 37 38 Metamodel mm = entityManagerFactory.getMetamodel(); 39 40 Set<ManagedType<?>> managedTypes = mm.getManagedTypes(); 41 assertEquals(managedTypes.size(), 1); 42 43 ManagedType itemType = managedTypes.iterator().next(); 44 assertEquals( 45 itemType.getPersistenceType(), 46 Type.PersistenceType.ENTITY 47 ); 48 49 SingularAttribute nameAttribute = 50 itemType.getSingularAttribute("name"); 51 assertEquals( 52 nameAttribute.getJavaType(), 53 String.class 54 ); 55 assertEquals( 56 nameAttribute.getPersistentAttributeType(), 57 Attribute.PersistentAttributeType.BASIC 58 ); 59 assertFalse( 60 nameAttribute.isOptional() // NOT NULL 61 ); 62 63 SingularAttribute auctionEndAttribute = 64 itemType.getSingularAttribute("auctionEnd"); 65 assertEquals( 66 auctionEndAttribute.getJavaType(), 67 Date.class 68 ); 69 assertFalse( 70 auctionEndAttribute.isCollection() 71 ); 72 assertFalse( 73 auctionEndAttribute.isAssociation() 74 ); 75 } 76 77 /* @Test 78 public void accessStaticMetamodel() throws Exception { 79 80 SingularAttribute nameAttribute = Item_.name; 81 82 assertEquals( 83 nameAttribute.getJavaType(), 84 String.class 85 ); 86 }*/ 87 88 @Test 89 public void queryStaticMetamodel() throws Exception { 90 UserTransaction tx = TM.getUserTransaction(); 91 try { 92 tx.begin(); 93 94 EntityManager entityManager = JPA.createEntityManager(); 95 96 Item itemOne = new Item(); 97 itemOne.setName("This is some item"); 98 itemOne.setAuctionEnd(new Date(System.currentTimeMillis() + 100000)); 99 entityManager.persist(itemOne); 100 101 Item itemTwo = new Item(); 102 itemTwo.setName("Another item"); 103 itemTwo.setAuctionEnd(new Date(System.currentTimeMillis() + 100000)); 104 105 entityManager.persist(itemTwo); 106 107 tx.commit(); 108 entityManager.close(); 109 110 entityManager = JPA.createEntityManager(); 111 tx.begin(); 112 113 CriteriaBuilder cb = entityManager.getCriteriaBuilder(); 114 115 // This query is the equivalent of "select i from Item i" 116 CriteriaQuery<Item> query = cb.createQuery(Item.class); 117 Root<Item> fromItem = query.from(Item.class); 118 query.select(fromItem); 119 120 List<Item> items = 121 entityManager.createQuery(query) 122 .getResultList(); 123 124 assertEquals(items.size(), 2); 125 126 // "where i.name like :pattern" 127 Path<String> namePath = fromItem.get("name"); 128 query.where( 129 cb.like( 130 namePath, // Has to be a Path<String> for like() operator! 131 cb.parameter(String.class, "pattern") 132 ) 133 ); 134 135 items = 136 entityManager.createQuery(query) 137 .setParameter("pattern", "%some item%") // Wildcards! 138 .getResultList(); 139 140 assertEquals(items.size(), 1); 141 assertEquals(items.iterator().next().getName(), "This is some item"); 142 143 // query.where( 144 // cb.like( 145 // fromItem.get(Item_.name), // Static Item_ metamodel! 146 // cb.parameter(String.class, "pattern") 147 // ) 148 // ); 149 150 items = 151 entityManager.createQuery(query) 152 .setParameter("pattern", "%some item%") // Wildcard! 153 .getResultList(); 154 155 assertEquals(items.size(), 1); 156 assertEquals(items.iterator().next().getName(), "This is some item"); 157 158 tx.commit(); 159 entityManager.close(); 160 } finally { 161 TM.rollback(); 162 } 163 } 164 165 }