JavaPersistenceWithHibernate第二版笔记-第五章-Mapping value types-005控制类型映射(Nationalized、@LOB、@org.hibernate.annotations.Type)
一、简介
1.
2.
3.
4.
to override this default mapping. The JPA specification has a convenient shortcut annotation for this purpose, @Lob
1 @Entity 2 public class Item { 3 @Lob 4 protected byte[] image; 5 @Lob 6 protected String description; 7 // ... 8 }
This maps the byte[] to an SQL BLOB data type and the String to a CLOB . Unfortunately, you still don’t get lazy loading with this design.
Alternatively, you can switch the type of property in your Java class. JDBC supports locator objects ( LOB s) directly. If your Java property is java.sql.Clob or java .sql.Blob , you get lazy loading without bytecode instrumentation:
1 @Entity 2 public class Item { 3 @Lob 4 protected java.sql.Blob imageBlob; 5 @Lob 6 protected java.sql.Clob description; 7 // ... 8 }
5.自定义类型
1 @Entity 2 public class Item { 3 @org.hibernate.annotations.Type(type = "yes_no") 4 protected boolean verified = false; 5 }
或
1 metaBuilder.applyBasicType(new MyUserType(), new String[]{"date"});
二、代码
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 }
三、测试代码
1 package org.jpwh.test.advanced; 2 3 import org.hibernate.Session; 4 import org.hibernate.engine.jdbc.StreamUtils; 5 import org.jpwh.env.JPATest; 6 import org.jpwh.model.advanced.Item; 7 import org.testng.annotations.Test; 8 9 import javax.persistence.EntityManager; 10 import javax.transaction.UserTransaction; 11 import java.io.ByteArrayInputStream; 12 import java.io.ByteArrayOutputStream; 13 import java.io.InputStream; 14 import java.sql.Blob; 15 import java.util.Random; 16 17 import static org.testng.Assert.assertEquals; 18 19 public class LazyProperties extends JPATest { 20 21 @Override 22 public void configurePersistenceUnit() throws Exception { 23 configurePersistenceUnit("AdvancedPU"); 24 } 25 26 @Test 27 public void storeLoadProperties() throws Exception { 28 UserTransaction tx = TM.getUserTransaction(); 29 try { 30 tx.begin(); 31 EntityManager em = JPA.createEntityManager(); 32 Item someItem = new Item(); 33 someItem.setName("Some item"); 34 someItem.setDescription("This is some description."); 35 byte[] bytes = new byte[131072]; 36 new Random().nextBytes(bytes); 37 someItem.setImage(bytes); 38 em.persist(someItem); 39 tx.commit(); 40 em.close(); 41 Long ITEM_ID = someItem.getId(); 42 43 tx.begin(); 44 em = JPA.createEntityManager(); 45 46 Item item = em.find(Item.class, ITEM_ID); 47 48 // Accessing one initializes ALL lazy properties in a single SELECT 49 assertEquals(item.getDescription(), "This is some description."); 50 assertEquals(item.getImage().length, 131072); // 128 kilobytes 51 52 tx.commit(); 53 em.close(); 54 } finally { 55 TM.rollback(); 56 } 57 } 58 59 @Test 60 public void storeLoadLocator() throws Exception { 61 // TODO: This test fails on H2 standalone 62 // http://groups.google.com/group/h2-database/browse_thread/thread/9c6f4893a62c9b1a 63 UserTransaction tx = TM.getUserTransaction(); 64 try { 65 tx.begin(); 66 EntityManager em = JPA.createEntityManager(); 67 68 byte[] bytes = new byte[131072]; 69 new Random().nextBytes(bytes); 70 InputStream imageInputStream = new ByteArrayInputStream(bytes); 71 int byteLength = bytes.length; 72 73 Item someItem = new Item(); 74 someItem.setName("Some item"); 75 someItem.setDescription("This is some description."); 76 77 // Need the native Hibernate API 78 Session session = em.unwrap(Session.class); 79 // You need to know the number of bytes you want to read from the stream! 80 Blob blob = session.getLobHelper() 81 .createBlob(imageInputStream, byteLength); 82 83 someItem.setImageBlob(blob); 84 em.persist(someItem); 85 86 tx.commit(); 87 em.close(); 88 89 Long ITEM_ID = someItem.getId(); 90 91 tx.begin(); 92 em = JPA.createEntityManager(); 93 94 Item item = em.find(Item.class, ITEM_ID); 95 96 // You can stream the bytes directly... 97 InputStream imageDataStream = item.getImageBlob().getBinaryStream(); 98 99 // ... or materialize them into memory: 100 ByteArrayOutputStream outStream = new ByteArrayOutputStream(); 101 StreamUtils.copy(imageDataStream, outStream); 102 byte[] imageBytes = outStream.toByteArray(); 103 assertEquals(imageBytes.length, 131072); 104 105 tx.commit(); 106 em.close(); 107 } finally { 108 TM.rollback(); 109 } 110 } 111 112 113 }
1 <persistence-unit name="AdvancedPU"> 2 <jta-data-source>myDS</jta-data-source> 3 <class>org.jpwh.model</class> 4 <class>org.jpwh.model.advanced.Item</class> 5 <class>org.jpwh.model.advanced.Bid</class> 6 <class>org.jpwh.model.advanced.User</class> 7 <class>org.jpwh.model.advanced.Address</class> 8 <class>org.jpwh.model.advanced.City</class> 9 <class>org.jpwh.model.advanced.ItemBidSummary</class> 10 <exclude-unlisted-classes>true</exclude-unlisted-classes> 11 <!-- 12 <properties> 13 <property name="hibernate.hbm2ddl.auto" value="create-drop"/> 14 </properties> 15 --> 16 </persistence-unit>
You can do anything you set your mind to, man!