JavaPersistenceWithHibernate第二版笔记-第四章-Mapping persistent classes-003映射实体时的可选操作(<delimited-identifiers/>、PhysicalNamingStrategy、PhysicalNamingStrategyStandardImpl、、、)
一、自定义映射的表名
1.
1 @Entity 2 @Table(name = "USERS") 3 public class User implements Serializable { 4 // ... 5 }
2.用定界符
1 //@Table(name = "`USER`")的标准 2 @Table(name = "`USER`") 3 4 //JPA的标准 5 @Table(name = "\"USER\"")
若全部SQL都加定界符, create an orm.xml file and add the setting <delimited-identifiers/> to its <persistence-unit-defaults> section,Hibernate then enforces quoted identifiers everywhere.
3.若要映射的表都有前缀,则可用实现PhysicalNamingStrategy接口或继承已有的实现
1 package org.jpwh.shared; 2 3 import org.hibernate.boot.model.naming.Identifier; 4 import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; 5 6 /** 7 * Prefixes all SQL table names with "CE_", for CaveatEmptor. 8 */ 9 public class CENamingStrategy extends 10 org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl { 11 12 @Override 13 public Identifier toPhysicalTableName(Identifier name, 14 JdbcEnvironment context) { 15 return new Identifier("CE_" + name.getText(), name.isQuoted()); 16 } 17 18 }
You have to enable the naming-strategy implementation in persistence.xml:
1 <persistence-unit>name="CaveatEmptorPU"> 2 ... 3 <properties> 4 <property name="hibernate.physical_naming_strategy" value="org.jpwh.shared.CENamingStrategy" /> 5 </properties> 6 </persistence-unit>
4.用ImplicitNamingStrategy
二、自定义实体名
1.
1 package my.other.model; 2 @javax.persistence.Entity(name = "AuctionItem") 3 public class Item { 4 // ... 5 }
三、动态生成SQL
To disable generation of INSERT and UPDATE SQL statements on startup, you need native Hibernate annotations:
1 @Entity 2 @org.hibernate.annotations.DynamicInsert 3 @org.hibernate.annotations.DynamicUpdate 4 public class Item { 5 // ... 6 }
四、实体相关选项
1.Making an entity immutable
1 @Entity 2 @org.hibernate.annotations.Immutable 3 public class Bid { 4 // ... 5 }
2.Mapping an entity to a subselect
When an instance of ItemBidSummary is loaded, Hibernate executes your custom SQL
SELECT as a subselect:
Synchronize注解的作用:Hibernate will then know it has to flush modifications of Item and Bid instances
before it executes a query against ItemBidSummary
1 package org.jpwh.model.advanced; 2 3 import javax.persistence.Entity; 4 import javax.persistence.Id; 5 6 @Entity 7 @org.hibernate.annotations.Immutable 8 @org.hibernate.annotations.Subselect( 9 value = "select i.ID as ITEMID, i.ITEM_NAME as NAME, " + 10 "count(b.ID) as NUMBEROFBIDS " + 11 "from ITEM i left outer join BID b on i.ID = b.ITEM_ID " + 12 "group by i.ID, i.ITEM_NAME" 13 ) 14 15 // TODO Table names are case sensitive, Hibernate bug HHH-8430 16 @org.hibernate.annotations.Synchronize({"Item", "Bid"}) 17 public class ItemBidSummary { 18 19 @Id 20 protected Long itemId; 21 22 protected String name; 23 24 protected long numberOfBids; 25 26 public ItemBidSummary() { 27 } 28 29 // Getter methods... 30 public Long getItemId() { 31 return itemId; 32 } 33 34 public String getName() { 35 return name; 36 } 37 38 public long getNumberOfBids() { 39 return numberOfBids; 40 } 41 42 // ... 43 }
五、测试代码
1.
1 package org.jpwh.test.advanced; 2 3 import org.jpwh.env.JPATest; 4 import org.jpwh.model.advanced.Bid; 5 import org.jpwh.model.advanced.Item; 6 import org.jpwh.model.advanced.ItemBidSummary; 7 import org.testng.annotations.Test; 8 9 import javax.persistence.EntityManager; 10 import javax.persistence.Query; 11 import javax.transaction.UserTransaction; 12 import java.math.BigDecimal; 13 14 import static org.testng.Assert.assertEquals; 15 16 public class MappedSubselect extends JPATest { 17 18 @Override 19 public void configurePersistenceUnit() throws Exception { 20 configurePersistenceUnit("AdvancedPU"); 21 } 22 23 @Test 24 public void loadSubselectEntity() throws Exception { 25 long ITEM_ID = storeItemAndBids(); 26 27 UserTransaction tx = TM.getUserTransaction(); 28 try { 29 tx.begin(); 30 EntityManager em = JPA.createEntityManager(); 31 32 { 33 ItemBidSummary itemBidSummary = em.find(ItemBidSummary.class, ITEM_ID); 34 // select * from ( 35 // select i.ID as ITEMID, i.ITEM_NAME as NAME, ... 36 // ) where ITEMID = ? 37 38 assertEquals(itemBidSummary.getName(), "AUCTION: Some item"); 39 } 40 em.clear(); 41 42 { // Hibernate will synchronize the right tables before querying 43 Item item = em.find(Item.class, ITEM_ID); 44 item.setName("New name"); 45 46 // No flush before retrieval by identifier! 47 //ItemBidSummary itemBidSummary = em.find(ItemBidSummary.class, ITEM_ID); 48 49 // Automatic flush before queries if synchronized tables are affected! 50 Query query = em.createQuery( 51 "select ibs from ItemBidSummary ibs where ibs.itemId = :id" 52 ); 53 ItemBidSummary itemBidSummary = 54 (ItemBidSummary)query.setParameter("id", ITEM_ID).getSingleResult(); 55 56 assertEquals(itemBidSummary.getName(), "AUCTION: New name"); 57 } 58 59 tx.commit(); 60 em.close(); 61 } finally { 62 TM.rollback(); 63 } 64 } 65 66 public Long storeItemAndBids() throws Exception { 67 UserTransaction tx = TM.getUserTransaction(); 68 tx.begin(); 69 EntityManager em = JPA.createEntityManager(); 70 Item item = new Item(); 71 item.setName("Some item"); 72 item.setDescription("This is some description."); 73 em.persist(item); 74 for (int i = 1; i <= 3; i++) { 75 Bid bid = new Bid(); 76 bid.setAmount(new BigDecimal(10 + i)); 77 bid.setItem(item); 78 em.persist(bid); 79 } 80 tx.commit(); 81 em.close(); 82 return item.getId(); 83 } 84 85 }
Note that Hibernate doesn’t flush automatically before a find() operation—only before a Query is executed, if necessary. Hibernate detects that the modified Item will affect the result of the query, because the ITEM table is synchronized with ItemBid-Summary . Hence, a flush and the UPDATE of the ITEM row are necessary to avoid the
query returning stale data.
2.
1 package org.jpwh.model.advanced; 2 3 import org.jpwh.model.Constants; 4 5 import javax.persistence.Access; 6 import javax.persistence.AccessType; 7 import javax.persistence.Basic; 8 import javax.persistence.Column; 9 import javax.persistence.Entity; 10 import javax.persistence.EnumType; 11 import javax.persistence.Enumerated; 12 import javax.persistence.FetchType; 13 import javax.persistence.GeneratedValue; 14 import javax.persistence.Id; 15 import javax.persistence.Lob; 16 import javax.persistence.Temporal; 17 import javax.persistence.TemporalType; 18 import javax.validation.constraints.NotNull; 19 import java.math.BigDecimal; 20 import java.sql.Blob; 21 import java.util.Date; 22 23 @Entity 24 public class Item { 25 26 /* 27 The <code>Item</code> entity defaults to field access, the <code>@Id</code> is on a field. (We 28 have also moved the brittle <code>ID_GENERATOR</code> string into a constant.) 29 */ 30 @Id 31 @GeneratedValue(generator = Constants.ID_GENERATOR) 32 protected Long id; 33 34 @org.hibernate.annotations.Type(type = "yes_no") 35 protected boolean verified = false; 36 37 // JPA says @Temporal is required but Hibernate will default to TIMESTAMP without it 38 @Temporal(TemporalType.TIMESTAMP) 39 @Column(updatable = false) 40 @org.hibernate.annotations.CreationTimestamp 41 protected Date createdOn; 42 43 // Java 8 API 44 // protected Instant reviewedOn; 45 46 @NotNull 47 @Basic(fetch = FetchType.LAZY) // Defaults to EAGER 48 protected String description; 49 50 @Basic(fetch = FetchType.LAZY) 51 @Column(length = 131072) // 128 kilobyte maximum for the picture 52 protected byte[] image; // Maps to SQL VARBINARY type 53 54 @Lob 55 protected Blob imageBlob; 56 57 @NotNull 58 @Enumerated(EnumType.STRING) // Defaults to ORDINAL 59 protected AuctionType auctionType = AuctionType.HIGHEST_BID; 60 61 @org.hibernate.annotations.Formula( 62 "substr(DESCRIPTION, 1, 12) || '...'" 63 ) 64 protected String shortDescription; 65 66 @org.hibernate.annotations.Formula( 67 "(select avg(b.AMOUNT) from BID b where b.ITEM_ID = ID)" 68 ) 69 protected BigDecimal averageBidAmount; 70 71 @Column(name = "IMPERIALWEIGHT") 72 @org.hibernate.annotations.ColumnTransformer( 73 read = "IMPERIALWEIGHT / 2.20462", 74 write = "? * 2.20462" 75 ) 76 protected double metricWeight; 77 78 @Temporal(TemporalType.TIMESTAMP) 79 @Column(insertable = false, updatable = false) 80 @org.hibernate.annotations.Generated( 81 org.hibernate.annotations.GenerationTime.ALWAYS 82 ) 83 protected Date lastModified; 84 85 @Column(insertable = false) 86 @org.hibernate.annotations.ColumnDefault("1.00") 87 @org.hibernate.annotations.Generated( 88 org.hibernate.annotations.GenerationTime.INSERT 89 ) 90 protected BigDecimal initialPrice; 91 92 /* 93 The <code>@Access(AccessType.PROPERTY)</code> setting on the <code>name</code> field switches this 94 particular property to runtime access through getter/setter methods by the JPA provider. 95 */ 96 @Access(AccessType.PROPERTY) 97 @Column(name = "ITEM_NAME") // Mappings are still expected here! 98 protected String name; 99 100 /* 101 Hibernate will call <code>getName()</code> and <code>setName()</code> when loading and storing items. 102 */ 103 public String getName() { 104 return name; 105 } 106 107 public void setName(String name) { 108 this.name = 109 !name.startsWith("AUCTION: ") ? "AUCTION: " + name : name; 110 } 111 112 public Long getId() { // Optional but useful 113 return id; 114 } 115 116 public String getDescription() { 117 return description; 118 } 119 120 public void setDescription(String description) { 121 this.description = description; 122 } 123 124 public String getShortDescription() { 125 return shortDescription; 126 } 127 128 public BigDecimal getAverageBidAmount() { 129 return averageBidAmount; 130 } 131 132 public double getMetricWeight() { 133 return metricWeight; 134 } 135 136 public void setMetricWeight(double metricWeight) { 137 this.metricWeight = metricWeight; 138 } 139 140 public Date getLastModified() { 141 return lastModified; 142 } 143 144 public BigDecimal getInitialPrice() { 145 return initialPrice; 146 } 147 148 public Date getCreatedOn() { 149 return createdOn; 150 } 151 152 public boolean isVerified() { 153 return verified; 154 } 155 156 public void setVerified(boolean verified) { 157 this.verified = verified; 158 } 159 160 public byte[] getImage() { 161 return image; 162 } 163 164 public void setImage(byte[] image) { 165 this.image = image; 166 } 167 168 public Blob getImageBlob() { 169 return imageBlob; 170 } 171 172 public void setImageBlob(Blob imageBlob) { 173 this.imageBlob = imageBlob; 174 } 175 176 public AuctionType getAuctionType() { 177 return auctionType; 178 } 179 180 public void setAuctionType(AuctionType auctionType) { 181 this.auctionType = auctionType; 182 } 183 }
3.
1 package org.jpwh.model.advanced; 2 3 import org.jpwh.model.Constants; 4 5 import javax.persistence.Entity; 6 import javax.persistence.FetchType; 7 import javax.persistence.GeneratedValue; 8 import javax.persistence.Id; 9 import javax.persistence.JoinColumn; 10 import javax.persistence.ManyToOne; 11 import javax.validation.constraints.NotNull; 12 import java.math.BigDecimal; 13 14 @Entity 15 @org.hibernate.annotations.Immutable 16 public class Bid { 17 18 @Id 19 @GeneratedValue(generator = Constants.ID_GENERATOR) 20 protected Long id; 21 22 @NotNull 23 protected BigDecimal amount; 24 25 @ManyToOne(optional = false, fetch = FetchType.LAZY) // NOT NULL 26 @JoinColumn(name = "ITEM_ID") // Actually the default name 27 protected Item item; 28 29 public Long getId() { 30 return id; 31 } 32 33 public BigDecimal getAmount() { 34 return amount; 35 } 36 37 public void setAmount(BigDecimal amount) { 38 this.amount = amount; 39 } 40 41 public Item getItem() { 42 return item; 43 } 44 45 public void setItem(Item item) { 46 this.item = item; 47 } 48 }