JavaPersistenceWithHibernate第二版笔记-第四章-Mapping persistent classes-003映射实体时的可选操作(、PhysicalNamingStrategy、PhysicalNamingStrategyStandardImpl、、、)

时间:2023-03-09 18:57:55
JavaPersistenceWithHibernate第二版笔记-第四章-Mapping persistent classes-003映射实体时的可选操作(<delimited-identifiers/>、PhysicalNamingStrategy、PhysicalNamingStrategyStandardImpl、、、)

一、自定义映射的表名

1.

 @Entity
@Table(name = "USERS")
public class User implements Serializable {
// ...
}

2.用定界符

 //@Table(name = "`USER`")的标准
@Table(name = "`USER`") //JPA的标准
@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接口或继承已有的实现

 package org.jpwh.shared;

 import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; /**
* Prefixes all SQL table names with "CE_", for CaveatEmptor.
*/
public class CENamingStrategy extends
org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl { @Override
public Identifier toPhysicalTableName(Identifier name,
JdbcEnvironment context) {
return new Identifier("CE_" + name.getText(), name.isQuoted());
} }

You have to enable the naming-strategy implementation in persistence.xml:

 <persistence-unit>name="CaveatEmptorPU">
...
<properties>
<property name="hibernate.physical_naming_strategy" value="org.jpwh.shared.CENamingStrategy" />
</properties>
</persistence-unit>

4.用ImplicitNamingStrategy

二、自定义实体名

1.

 package my.other.model;
@javax.persistence.Entity(name = "AuctionItem")
public class Item {
// ...
}

三、动态生成SQL

To disable generation of INSERT and UPDATE SQL statements on startup, you need native Hibernate annotations:

 @Entity
@org.hibernate.annotations.DynamicInsert
@org.hibernate.annotations.DynamicUpdate
public class Item {
// ...
}

四、实体相关选项

1.Making an entity immutable

 @Entity
@org.hibernate.annotations.Immutable
public class Bid {
// ...
}

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

 package org.jpwh.model.advanced;

 import javax.persistence.Entity;
import javax.persistence.Id; @Entity
@org.hibernate.annotations.Immutable
@org.hibernate.annotations.Subselect(
value = "select i.ID as ITEMID, i.ITEM_NAME as NAME, " +
"count(b.ID) as NUMBEROFBIDS " +
"from ITEM i left outer join BID b on i.ID = b.ITEM_ID " +
"group by i.ID, i.ITEM_NAME"
) // TODO Table names are case sensitive, Hibernate bug HHH-8430
@org.hibernate.annotations.Synchronize({"Item", "Bid"})
public class ItemBidSummary { @Id
protected Long itemId; protected String name; protected long numberOfBids; public ItemBidSummary() {
} // Getter methods...
public Long getItemId() {
return itemId;
} public String getName() {
return name;
} public long getNumberOfBids() {
return numberOfBids;
} // ...
}

五、测试代码

1.

 package org.jpwh.test.advanced;

 import org.jpwh.env.JPATest;
import org.jpwh.model.advanced.Bid;
import org.jpwh.model.advanced.Item;
import org.jpwh.model.advanced.ItemBidSummary;
import org.testng.annotations.Test; import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.transaction.UserTransaction;
import java.math.BigDecimal; import static org.testng.Assert.assertEquals; public class MappedSubselect extends JPATest { @Override
public void configurePersistenceUnit() throws Exception {
configurePersistenceUnit("AdvancedPU");
} @Test
public void loadSubselectEntity() throws Exception {
long ITEM_ID = storeItemAndBids(); UserTransaction tx = TM.getUserTransaction();
try {
tx.begin();
EntityManager em = JPA.createEntityManager(); {
ItemBidSummary itemBidSummary = em.find(ItemBidSummary.class, ITEM_ID);
// select * from (
// select i.ID as ITEMID, i.ITEM_NAME as NAME, ...
// ) where ITEMID = ? assertEquals(itemBidSummary.getName(), "AUCTION: Some item");
}
em.clear(); { // Hibernate will synchronize the right tables before querying
Item item = em.find(Item.class, ITEM_ID);
item.setName("New name"); // No flush before retrieval by identifier!
//ItemBidSummary itemBidSummary = em.find(ItemBidSummary.class, ITEM_ID); // Automatic flush before queries if synchronized tables are affected!
Query query = em.createQuery(
"select ibs from ItemBidSummary ibs where ibs.itemId = :id"
);
ItemBidSummary itemBidSummary =
(ItemBidSummary)query.setParameter("id", ITEM_ID).getSingleResult(); assertEquals(itemBidSummary.getName(), "AUCTION: New name");
} tx.commit();
em.close();
} finally {
TM.rollback();
}
} public Long storeItemAndBids() throws Exception {
UserTransaction tx = TM.getUserTransaction();
tx.begin();
EntityManager em = JPA.createEntityManager();
Item item = new Item();
item.setName("Some item");
item.setDescription("This is some description.");
em.persist(item);
for (int i = 1; i <= 3; i++) {
Bid bid = new Bid();
bid.setAmount(new BigDecimal(10 + i));
bid.setItem(item);
em.persist(bid);
}
tx.commit();
em.close();
return item.getId();
} }

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.

 package org.jpwh.model.advanced;

 import org.jpwh.model.Constants;

 import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.sql.Blob;
import java.util.Date; @Entity
public class Item { /*
The <code>Item</code> entity defaults to field access, the <code>@Id</code> is on a field. (We
have also moved the brittle <code>ID_GENERATOR</code> string into a constant.)
*/
@Id
@GeneratedValue(generator = Constants.ID_GENERATOR)
protected Long id; @org.hibernate.annotations.Type(type = "yes_no")
protected boolean verified = false; // JPA says @Temporal is required but Hibernate will default to TIMESTAMP without it
@Temporal(TemporalType.TIMESTAMP)
@Column(updatable = false)
@org.hibernate.annotations.CreationTimestamp
protected Date createdOn; // Java 8 API
// protected Instant reviewedOn; @NotNull
@Basic(fetch = FetchType.LAZY) // Defaults to EAGER
protected String description; @Basic(fetch = FetchType.LAZY)
@Column(length = 131072) // 128 kilobyte maximum for the picture
protected byte[] image; // Maps to SQL VARBINARY type @Lob
protected Blob imageBlob; @NotNull
@Enumerated(EnumType.STRING) // Defaults to ORDINAL
protected AuctionType auctionType = AuctionType.HIGHEST_BID; @org.hibernate.annotations.Formula(
"substr(DESCRIPTION, 1, 12) || '...'"
)
protected String shortDescription; @org.hibernate.annotations.Formula(
"(select avg(b.AMOUNT) from BID b where b.ITEM_ID = ID)"
)
protected BigDecimal averageBidAmount; @Column(name = "IMPERIALWEIGHT")
@org.hibernate.annotations.ColumnTransformer(
read = "IMPERIALWEIGHT / 2.20462",
write = "? * 2.20462"
)
protected double metricWeight; @Temporal(TemporalType.TIMESTAMP)
@Column(insertable = false, updatable = false)
@org.hibernate.annotations.Generated(
org.hibernate.annotations.GenerationTime.ALWAYS
)
protected Date lastModified; @Column(insertable = false)
@org.hibernate.annotations.ColumnDefault("1.00")
@org.hibernate.annotations.Generated(
org.hibernate.annotations.GenerationTime.INSERT
)
protected BigDecimal initialPrice; /*
The <code>@Access(AccessType.PROPERTY)</code> setting on the <code>name</code> field switches this
particular property to runtime access through getter/setter methods by the JPA provider.
*/
@Access(AccessType.PROPERTY)
@Column(name = "ITEM_NAME") // Mappings are still expected here!
protected String name; /*
Hibernate will call <code>getName()</code> and <code>setName()</code> when loading and storing items.
*/
public String getName() {
return name;
} public void setName(String name) {
this.name =
!name.startsWith("AUCTION: ") ? "AUCTION: " + name : name;
} public Long getId() { // Optional but useful
return id;
} public String getDescription() {
return description;
} public void setDescription(String description) {
this.description = description;
} public String getShortDescription() {
return shortDescription;
} public BigDecimal getAverageBidAmount() {
return averageBidAmount;
} public double getMetricWeight() {
return metricWeight;
} public void setMetricWeight(double metricWeight) {
this.metricWeight = metricWeight;
} public Date getLastModified() {
return lastModified;
} public BigDecimal getInitialPrice() {
return initialPrice;
} public Date getCreatedOn() {
return createdOn;
} public boolean isVerified() {
return verified;
} public void setVerified(boolean verified) {
this.verified = verified;
} public byte[] getImage() {
return image;
} public void setImage(byte[] image) {
this.image = image;
} public Blob getImageBlob() {
return imageBlob;
} public void setImageBlob(Blob imageBlob) {
this.imageBlob = imageBlob;
} public AuctionType getAuctionType() {
return auctionType;
} public void setAuctionType(AuctionType auctionType) {
this.auctionType = auctionType;
}
}

3.

 package org.jpwh.model.advanced;

 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
@org.hibernate.annotations.Immutable
public class Bid { @Id
@GeneratedValue(generator = Constants.ID_GENERATOR)
protected Long id; @NotNull
protected BigDecimal amount; @ManyToOne(optional = false, fetch = FetchType.LAZY) // NOT NULL
@JoinColumn(name = "ITEM_ID") // Actually the default name
protected Item item; public Long getId() {
return id;
} public BigDecimal getAmount() {
return amount;
} public void setAmount(BigDecimal amount) {
this.amount = amount;
} public Item getItem() {
return item;
} public void setItem(Item item) {
this.item = item;
}
}