一、本章主要介绍了
实体类型:
该类的实例有OID,一个实例对应表的一条记录,实例的OID就是该记录的主键。
一个实例可以被多个其他实体对象共享引用。 有独立的生命周期。可以被单独持久化。
值类型 :没有OID,不能被单独持久化,只能附属到一个依赖对象上,只能被依赖对象引用,有该依赖对象负责其生命周期。(没有OID就是没有主键,当然不能insert到表中了)。
一句话:
实体类型,在DB中代表一个表,一个实体代表一行记录;
值类型在DB中没有表,只代表一个column。
对象模型(域模型)有具体的程序代码写成的,为了提高代码的重用性,可能会把域模型拆分定义成多个类,在这多个类中,那些包含了主键的成员的类就是entity,它的OID对应的就是表中的主键,它可以直接单独insert到表中;而那些只对应包含非主键的成员变量的类,就是值类.值类作为成员变量定义在entity类中,持久化entity类时,entity实例被保存成一个row,它的值类成员被保存成row的column。
几种键
键: 键是唯一标识一个实体的一个或者多个数据属性。在物理数据库中,键可以由表的一个或者多个列组成,它们的值唯一标识关系表中的一行。
复合键: 由两个或者多个属性组成的键。
自然键: 由现实世界中已经存在的属性组成的键。例如,美国公民被分配了一个唯一(不保证一定正确,但实际上非常接近唯一)的社会保险号(SSN)。如果隐私法允许的话,SSN可能被用作Person实体的自然键(假设组织的人员范围仅限于美国)。
代理键: 完全没有商业含义,通常由当下的系统自动生成,都是单键。
自然键: 已经真实存在的键,通常具有商业含义,比如身份证ID,护照编码等等,可以是单键,也可以是复合键。
候选键: 在逻辑数据模型中的实体类型可能具有0或多个候选键,简称为唯一标识(注解:某些人不主张在逻辑数据模型中标识候选键,因此没有固定标准)。例如,假设我们只涉及美国公民,那么SSN是Person实体类型的一个候选键,同时姓名与电话号码的组合(假设组合是唯一的)是第二个可能的候选键。这两个键都被称作候选键是因为它们是物理数据模型中主键、次键或者非键的候选目标。
主键: 实体类型的首选键。
备用键: 也就是次键,是表中行的另一个唯一标识。
外键: 在一个实体类型中表示另一个实体类型的主键或者次键的一个或多个属性。
详解介绍参考这:https://ask.hellobi.com/blog/rayshawn/3257
二、代码
一些实体类:
其中User类中使用了作为包级别元数据配置的Hibernate标识符生成器:
package org.jpwh.model.advanced;
import org.jpwh.model.Constants;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name = "USERS")
public class User {
@Id
@GeneratedValue(generator = Constants.ID_GENERATOR)
protected Long id;
public Long getId() {
return id;
}
protected Address address;
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
/model/src/main/java/org/jpwh/model/package-info.java
@org.hibernate.annotations.GenericGenerator(
name = "ID_GENERATOR",
strategy = "enhanced-sequence",
parameters = {
@org.hibernate.annotations.Parameter(
name = "sequence_name",
value = "JPWH_SEQUENCE"
),
@org.hibernate.annotations.Parameter(
name = "initial_value",
value = "1000"
)
})
package org.jpwh.model;
而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;
}
// ...
}
测试类:
/examples/src/test/java/org/jpwh/test/advanced/MappedSubselect.java
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将在查询之前同步正确的表
Item item = em.find(Item.class, ITEM_ID);
item.setName("New name");
// 在检索标识符之前不刷新!
// ItemBidSummary itemBidSummary = em.find(ItemBidSummary.class, ITEM_ID);
// 如果同步表受影响,在查询之前自动刷新。
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();
}
}
/examples/src/test/java/org/jpwh/test/advanced/AccessType.java
package org.jpwh.test.advanced;
import org.jpwh.env.JPATest;
import org.jpwh.model.advanced.Item;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import javax.persistence.EntityManager;
import javax.transaction.UserTransaction;
import static org.testng.Assert.assertEquals;
public class AccessType extends JPATest {
@Override
public void configurePersistenceUnit() throws Exception {
configurePersistenceUnit("AdvancedPU");
}
@Test
public void storeLoadAccessType() throws Exception {
UserTransaction tx = TM.getUserTransaction();
try {
tx.begin();
EntityManager em = JPA.createEntityManager();
Item someItem = new Item();
someItem.setName("Some item");
someItem.setDescription("This is some description.");
em.persist(someItem);
tx.commit();
em.close();
Long ITEM_ID = someItem.getId();
tx.begin();
em = JPA.createEntityManager();
Item item = em.find(Item.class, ITEM_ID);
assertEquals(item.getName(), "AUCTION: Some item");
tx.commit();
em.close();
} finally {
TM.rollback();
}
}
}
/examples/src/test/java/org/jpwh/test/advanced/BooleanOverride.java
package org.jpwh.test.advanced;
import org.jpwh.env.JPATest;
import org.jpwh.model.advanced.Item;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import javax.persistence.EntityManager;
import javax.transaction.UserTransaction;
import static org.testng.Assert.assertTrue;
public class BooleanOverride extends JPATest {
@BeforeClass
@java.lang.Override
public void configurePersistenceUnit() throws Exception {
configurePersistenceUnit("AdvancedPU");
}
@Test
public void storeLoadOverride() throws Exception {
UserTransaction tx = TM.getUserTransaction();
try {
tx.begin();
EntityManager em = JPA.createEntityManager();
Item someItem = new Item();
someItem.setName("Some item");
someItem.setDescription("This is some description.");
someItem.setVerified(true);
em.persist(someItem);
tx.commit();
em.close();
Long ITEM_ID = someItem.getId();
tx.begin();
em = JPA.createEntityManager();
Item item = em.find(Item.class, ITEM_ID);
assertTrue(item.isVerified());
tx.commit();
em.close();
} finally {
TM.rollback();
}
}
}
/examples/src/test/java/org/jpwh/test/advanced/DerivedProperties.java
package org.jpwh.test.advanced;
import org.jpwh.env.JPATest;
import org.jpwh.model.advanced.Bid;
import org.jpwh.model.advanced.Item;
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 DerivedProperties extends JPATest {
@Override
public void configurePersistenceUnit() throws Exception {
configurePersistenceUnit("AdvancedPU");
}
@Test
public void storeLoadFormula() throws Exception {
long ITEM_ID = storeItemAndBids();
UserTransaction tx = TM.getUserTransaction();
try {
tx.begin();
EntityManager em = JPA.createEntityManager();
Item item = em.find(Item.class, ITEM_ID);
assertEquals(item.getShortDescription(), "This is some...");
tx.commit();
em.close();
} finally {
TM.rollback();
}
}
@Test
public void storeLoadFormulaSubselect() throws Exception {
long ITEM_ID = storeItemAndBids();
UserTransaction tx = TM.getUserTransaction();
try {
tx.begin();
EntityManager em = JPA.createEntityManager();
Item item = em.find(Item.class, ITEM_ID);
assertEquals(item.getAverageBidAmount().compareTo(new BigDecimal("12")), 0);
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();
}
}
/examples/src/test/java/org/jpwh/test/advanced/GeneratedProperties.java
package org.jpwh.test.advanced;
import org.hibernate.Session;
import org.hibernate.jdbc.Work;
import org.jpwh.env.DatabaseProduct;
import org.jpwh.env.JPATest;
import org.jpwh.model.advanced.Bid;
import org.jpwh.model.advanced.Item;
import org.testng.annotations.Test;
import javax.persistence.EntityManager;
import javax.transaction.UserTransaction;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Date;
import static org.testng.Assert.*;
//生成的属性
public class GeneratedProperties extends JPATest {
@Override
public void configurePersistenceUnit() throws Exception {
configurePersistenceUnit("AdvancedPU");
}
@Override
public void afterJPABootstrap() throws Exception {
if (!TM.databaseProduct.equals(DatabaseProduct.H2)) return;
// LASTMODIFIED测试显示数据库触发器如何生成属性值,我们实际上需要一个触发器
try (Session session = JPA.createEntityManager().unwrap(Session.class)) {
session.doWork(
new Work() {
@Override
public void execute(Connection connection) throws SQLException {
Statement stat = connection.createStatement();
stat.execute("drop trigger if exists TRG_ITEM_LASTMODIFIED_INSERT");
stat.execute("create trigger TRG_ITEM_LASTMODIFIED_INSERT after insert on ITEM" +
" for each row call \"" + org.jpwh.shared.trigger.UpdateLastModifiedTrigger.class.getName() + "\"");
stat.execute("drop trigger if exists TRG_ITEM_LASTMODIFIED_UPDATE");
stat.execute("create trigger TRG_ITEM_LASTMODIFIED_UPDATE after update on ITEM" +
" for each row call \"" + org.jpwh.shared.trigger.UpdateLastModifiedTrigger.class.getName() + "\"");
stat.close();
}
}
);
}
}
@Test(groups = {"H2"})
public void storeLoadLastModified() throws Exception {
long ITEM_ID = storeItemAndBids();
UserTransaction tx = TM.getUserTransaction();
try {
tx.begin();
EntityManager em = JPA.createEntityManager();
Item item = em.find(Item.class, ITEM_ID);
assertNotNull(item.getCreatedOn());
Date lastModified = item.getLastModified();
assertNotNull(lastModified);
assertTrue(item.getCreatedOn().getTime() < lastModified.getTime());
item.setDescription("Some modification.");
em.flush();
Date newLastModified = item.getLastModified();
assertNotEquals(lastModified, newLastModified, "Modification time should have been updated");
tx.commit();
em.close();
} finally {
TM.rollback();
}
}
@Test
public void storeLoadInitialPrice() throws Exception {
long ITEM_ID = storeItemAndBids();
UserTransaction tx = TM.getUserTransaction();
try {
tx.begin();
EntityManager em = JPA.createEntityManager();
Item item = em.find(Item.class, ITEM_ID);
tx.commit();
em.close();
assertEquals(item.getInitialPrice().compareTo(new BigDecimal("1")), 0);
} 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();
}
}
/examples/src/test/java/org/jpwh/test/advanced/LazyProperties.java
package org.jpwh.test.advanced;
import org.hibernate.Session;
import org.hibernate.engine.jdbc.StreamUtils;
import org.jpwh.env.JPATest;
import org.jpwh.model.advanced.Item;
import org.testng.annotations.Test;
import javax.persistence.EntityManager;
import javax.transaction.UserTransaction;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.sql.Blob;
import java.util.Random;
import static org.testng.Assert.assertEquals;
public class LazyProperties extends JPATest {
@Override
public void configurePersistenceUnit() throws Exception {
configurePersistenceUnit("AdvancedPU");
}
@Test
public void storeLoadProperties() throws Exception {
UserTransaction tx = TM.getUserTransaction();
try {
tx.begin();
EntityManager em = JPA.createEntityManager();
Item someItem = new Item();
someItem.setName("Some item");
someItem.setDescription("This is some description.");
byte[] bytes = new byte[131072];
new Random().nextBytes(bytes);
someItem.setImage(bytes);
em.persist(someItem);
tx.commit();
em.close();
Long ITEM_ID = someItem.getId();
tx.begin();
em = JPA.createEntityManager();
Item item = em.find(Item.class, ITEM_ID);
// Accessing one initializes ALL lazy properties in a single SELECT
assertEquals(item.getDescription(), "This is some description.");
assertEquals(item.getImage().length, 131072); // 128 kilobytes
tx.commit();
em.close();
} finally {
TM.rollback();
}
}
@Test
public void storeLoadLocator() throws Exception {
// TODO: This test fails on H2 standalone
// http://groups.google.com/group/h2-database/browse_thread/thread/9c6f4893a62c9b1a
UserTransaction tx = TM.getUserTransaction();
try {
tx.begin();
EntityManager em = JPA.createEntityManager();
byte[] bytes = new byte[131072];
new Random().nextBytes(bytes);
InputStream imageInputStream = new ByteArrayInputStream(bytes);
int byteLength = bytes.length;
Item someItem = new Item();
someItem.setName("Some item");
someItem.setDescription("This is some description.");
// Need the native Hibernate API
Session session = em.unwrap(Session.class);
// You need to know the number of bytes you want to read from the stream!
Blob blob = session.getLobHelper()
.createBlob(imageInputStream, byteLength);
someItem.setImageBlob(blob);
em.persist(someItem);
tx.commit();
em.close();
Long ITEM_ID = someItem.getId();
tx.begin();
em = JPA.createEntityManager();
Item item = em.find(Item.class, ITEM_ID);
// You can stream the bytes directly...
InputStream imageDataStream = item.getImageBlob().getBinaryStream();
// ... or materialize them into memory:
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
StreamUtils.copy(imageDataStream, outStream);
byte[] imageBytes = outStream.toByteArray();
assertEquals(imageBytes.length, 131072);
tx.commit();
em.close();
} finally {
TM.rollback();
}
}
}
/examples/src/test/java/org/jpwh/test/advanced/NestedComponents.java
package org.jpwh.test.advanced;
import org.jpwh.env.JPATest;
import org.jpwh.model.advanced.Address;
import org.jpwh.model.advanced.City;
import org.jpwh.model.advanced.User;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import javax.persistence.EntityManager;
import javax.transaction.UserTransaction;
import java.util.Locale;
import static org.testng.Assert.assertEquals;
//嵌套组件
public class NestedComponents extends JPATest {
@Override
public void configurePersistenceUnit() throws Exception {
configurePersistenceUnit("AdvancedPU");
}
@Test
public void storeAndLoadUsers() throws Exception {
UserTransaction tx = TM.getUserTransaction();
try {
tx.begin();
EntityManager em = JPA.createEntityManager();
City city = new City();
city.setZipcode("12345");
city.setName("Some City");
city.setCountry(Locale.GERMANY.getCountry());
Address address = new Address();
address.setStreet("Some Street 123");
address.setCity(city);
User userOne = new User();
userOne.setAddress(address);
em.persist(userOne);
tx.commit();
em.close();
tx.begin();
em = JPA.createEntityManager();
User u = em.find(User.class, userOne.getId());
assertEquals(u.getAddress().getStreet(), "Some Street 123");
assertEquals(u.getAddress().getCity().getZipcode(), "12345");
assertEquals(u.getAddress().getCity().getCountry(), Locale.GERMANY.getCountry());
tx.commit();
em.close();
} finally {
TM.rollback();
}
}
}
/examples/src/test/java/org/jpwh/test/advanced/Temporal.java
package org.jpwh.test.advanced;
import org.jpwh.env.JPATest;
import org.jpwh.model.advanced.Item;
import org.testng.annotations.Test;
import javax.persistence.EntityManager;
import javax.transaction.UserTransaction;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import static org.testng.Assert.*;
//临时的
public class Temporal extends JPATest {
@Override
public void configurePersistenceUnit() throws Exception {
configurePersistenceUnit("AdvancedPU");
}
@Test
public void storeLoadTemporal() throws Exception {
UserTransaction tx = TM.getUserTransaction();
try {
tx.begin();
EntityManager em = JPA.createEntityManager();
Item someItem = new Item();
someItem.setName("Some item");
someItem.setDescription("This is some description.");
// someItem.setReviewedOn(Instant.now().plusSeconds(60)); // A future time
em.persist(someItem);
tx.commit();
em.close();
Long ITEM_ID = someItem.getId();
Date ORIGINAL_CREATION_DATE = someItem.getCreatedOn();
tx.begin();
em = JPA.createEntityManager();
Item item = em.find(Item.class, ITEM_ID);
// java.util.Date and java.sql.Timestamp are not symmetric!
assertFalse(item.getCreatedOn().equals(ORIGINAL_CREATION_DATE));
assertFalse(item.getCreatedOn().getClass().equals(ORIGINAL_CREATION_DATE.getClass()));
// 这是如何正确比较Java中的时间值...
assertEquals(ORIGINAL_CREATION_DATE.getTime(), item.getCreatedOn().getTime());
// 或者使用稍微令人讨厌但相当可怕的日历API
Calendar oldDate = new GregorianCalendar();
oldDate.setTime(ORIGINAL_CREATION_DATE);
Calendar newDate = new GregorianCalendar();
newDate.setTime(item.getCreatedOn());
assertEquals(oldDate, newDate);
// Or the Java 8 API
// assertTrue(item.getReviewedOn().isAfter(item.getCreatedOn().toInstant()));
tx.commit();
em.close();
} finally {
TM.rollback();
}
}
}
/examples/src/test/java/org/jpwh/test/advanced/TransformingColumns.java
package org.jpwh.test.advanced;
import org.hibernate.Session;
import org.hibernate.jdbc.Work;
import org.jpwh.env.JPATest;
import org.jpwh.model.advanced.Bid;
import org.jpwh.model.advanced.Item;
import org.testng.annotations.Test;
import javax.persistence.EntityManager;
import javax.transaction.UserTransaction;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
//转换列
public class TransformingColumns extends JPATest {
@Override
public void configurePersistenceUnit() throws Exception {
configurePersistenceUnit("AdvancedPU");
}
@Test
public void storeLoadTransform() throws Exception {
final long ITEM_ID = storeItemAndBids();
UserTransaction tx = TM.getUserTransaction();
try {
tx.begin();
EntityManager em = JPA.createEntityManager();
{
Item item = em.find(Item.class, ITEM_ID);
assertEquals(item.getMetricWeight(), 2.0);
final boolean[] tests = new boolean[1];
em.unwrap(Session.class).doWork(new Work() {
@Override
public void execute(Connection connection) throws SQLException {
PreparedStatement statement = null;
ResultSet result = null;
try {
statement = connection.prepareStatement(
"select IMPERIALWEIGHT from ITEM where ID = ?"
);
statement.setLong(1, ITEM_ID);
result = statement.executeQuery();
while (result.next()) {
Double imperialWeight = result.getDouble("IMPERIALWEIGHT");
assertEquals(imperialWeight, 4.40924);
tests[0] = true;
}
} finally {
if (result != null)
result.close();
if (statement != null)
statement.close();
}
}
});
assertTrue(tests[0]);
}
em.clear();
{
List<Item> result =
em.createQuery("select i from Item i where i.metricWeight = :w")
.setParameter("w", 2.0)
.getResultList();
assertEquals(result.size(), 1);
}
em.clear();
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.setMetricWeight(2);
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();
}
}