Intoduction : Accroding to book, write some examples to persist entity and value types.
Part One > The Persist Class
EntityValue.java
- This class contains many annotations, which are used to persist entity and value types.
- You can find 5 problems that i am still can’t resolve.
- To map embedded components and enumeration type, there are some extra classes needede.
/*
* Attribute : annotations in ation
*
* Function : test JPA & Hibernate annotaions range form entity to value type
*
* Problem : Line 58, Line 87, Line 121, Line 153, Line 196
*
* Author : Ray LEE
*
* Date : 2018.09.27
*/
package ray;
import java.util.Calendar;
import java.util.Date;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Id;
import javax.persistence.GeneratedValue;
import javax.persistence.Column;
import javax.persistence.Basic;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Enumerated;
import javax.persistence.EnumType;
import javax.persistence.AttributeOverrides;
import javax.persistence.AttributeOverride;
import javax.persistence.Embedded;
import javax.persistence.Convert;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
@Entity
//Override table name
@Table(name="EV")
public class EntityValue{
//Define your own id generator
@Id
@GeneratedValue(generator = "ID_GENERATOR")
@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 = "1997"
)
})
/* Problem :
* When i try to define GenericGenerator in package-info.java,
* There is some wrong with recognize this self-defination generator,
* Maven test will show error below --->
*
* testAppendAload(ray.EntityValueTest) Time elapsed: 1.519 sec <<< ERROR!
* org.hibernate.AnnotationException: Unknown Id.generator: ID_GENERATOR
*/
protected Long id;
//Make sure field is not null
@NotNull
protected String firstNull;
@Column(nullable = false)
protected String middleNull;
@Basic(optional = false)
protected String lastNull;
//Override column name
@NotNull
@Column(name="OVERRIDECOLUMN")
protected String overridedName;
//Validate field before persist it into DB
@NotNull
@Size(
min = 5,
max = 16,
message = "This name has a restricted length range from 5 to 16.")
/* Problem :
* Even i set the min length is 5, it looks like doesn,t work
* When i set its value with 'Val' which length is 3,
*
* EntityValue ev = new EntityValue();
* ev.setFirstNull("first");
* ev.setMiddleNull("middle");
* ev.setLastNull("last");
* ev.setOverridedName("Override");
* ev.setValidatedName("Val");
*
* Maven BUILD SUCCESS, and MySQL does exist this record.
*
* mysql> select * from EV;
* +------+-----------+----------+------------+----------------+---------------+
* | id | firstNull | lastNull | middleNull | OVERRIDECOLUMN | validatedName |
* +------+-----------+----------+------------+----------------+---------------+
* | 1997 | first | last | middle | Override | Val |
* +------+-----------+----------+------------+----------------+---------------+
*/
protected String validatedName;
//Doing some transfer job when transfer data before program and DB
@Column(name = "IMPERIALWEIGHT")
@org.hibernate.annotations.ColumnTransformer(
read = "IMPERIALWEIGHT / 2.20462",
write = "? * 2.20462"
)
protected double metricWeight;
//Define a time stamp tag to incline the time that this record is created
@Temporal(TemporalType.TIMESTAMP)
@Column(updatable = false)
@org.hibernate.annotations.CreationTimestamp //automate creating time stamp
/* Problem :
* When i use java.uitl.Date as the field type,
* It is so werid DB missed one hour.
*
* mysql> select * from EV;
* +------+---------------------+-----------+----------+----------------+------------+----------------+---------------+
* | id | createdOn | firstNull | lastNull | IMPERIALWEIGHT | middleNull | OVERRIDECOLUMN | validatedName |
* +------+---------------------+-----------+----------+----------------+------------+----------------+---------------+
* | 1997 | 2018-09-27 02:26:26 | first | last | 44.0262614 | middle | Override | Val |
* +------+---------------------+-----------+----------+----------------+------------+----------------+---------------+
*
* Actually, it is 2018-09-27 03:26:26 now !!
* So, i try another time notation java.time.LocalDateTime
* Unfortunately, Maven hinted me
* <@Temporal should only be set on a java.util.Date or java.util.Calendar property>
* Finally, i change it into java.util.Calendar,
* Everything is normal now !!
*
* mysql> select * from EV;
* +------+---------------------+-----------+----------+----------------+------------+----------------+---------------+
* | id | createdOn | firstNull | lastNull | IMPERIALWEIGHT | middleNull | OVERRIDECOLUMN | validatedName |
* +------+---------------------+-----------+----------+----------------+------------+----------------+---------------+
* | 1997 | 2018-09-27 15:29:29 | first | last | 44.0262614 | middle | Override | Val |
* +------+---------------------+-----------+----------+----------------+------------+----------------+---------------+
*/
protected Calendar createdOn;
//Mapping time
@Temporal(TemporalType.TIMESTAMP)
@Column(insertable = false, updatable = false) //hint this time takes charged by system
@org.hibernate.annotations.Generated(
org.hibernate.annotations.GenerationTime.ALWAYS) //generate a new time as long as record changed
/* Problem :
* The record's lastModified column in DB is null,
* I changed the notation into Calendar, it still does not work;
*
* mysql> select * from EV;
* +------+---------------------+-----------+--------------+----------+
* | id | createdOn | firstNull | lastModified | lastNull |
* +------+---------------------+-----------+--------------+----------+
* | 1997 | 2018-09-27 15:58:35 | first | NULL | last |
* +------+---------------------+-----------+--------------+----------+
*/
protected Date lastModified;
//Mapping enumerations
@NotNull
@Enumerated(EnumType.STRING)
protected Emoji emojiType = Emoji.CRY;
//Mapping embeddable componnets
/* Scenario one : reference class is embaddable
* You do not have to use any other annotations any more,
* Unless if you want to change the default attribute names.
*/
@AttributeOverrides({
@AttributeOverride(name = "contry",
column = @Column(name = "EMBEDDCONTRY")),
@AttributeOverride(name = "province",
column = @Column(name = "EMBEDDPROVINCE"))
})
protected Address address;
/* Scenario two : reference class is a third party POJO
* You must declare it with annotation
*/
@Embedded
@AttributeOverrides({
@AttributeOverride(name = "city",
column = @Column(name = "POJOCITY")),
@AttributeOverride(name = "town",
column = @Column(name = "POJOTOWN"))
})
protected AddressDetail addressDetail;
/* Problem :
* The converter class doesn't work, Maven shows below ---->
*
* Tests in error:
* testAppendAload(ray.EntityValueTest): Unable to instantiate AttributeConverter [ray.BigName]
* testAppendAload(ray.EntityValueTest)
*
* So i must remove this part of code to let program run.
*/
// //If you do not want to store a embedded class's all fields with many columns, convert it into one
// @NotNull
// @Convert(
// converter = BigName.class,
// disableConversion = false)
// @Column(name = "BIGNAME", length = 63)
// protected BigName bigname;
// public void setBigName(BigName bigname){
// this.bigname = bigname;
// }
// public BigName getBigName(){
// return bigname;
// }
public void setAddressDetail(AddressDetail addressDetail){
this.addressDetail = addressDetail;
}
public AddressDetail getAdrressDetail(){
return addressDetail;
}
public void setAddress(Address address){
this.address = address;
}
public Address getAddress(){
return address;
}
public Date getLastModified(){
return lastModified;
}
public Calendar getCreatedOn(){
return createdOn;
}
public double getMetricWeight(){
return metricWeight;
}
public void setMetricWeight(double metricWeight){
this.metricWeight = metricWeight;
}
public String getFirstNull() {
return firstNull;
}
public void setFirstNull(String firstNull) {
this.firstNull = firstNull;
}
public String getMiddleNull() {
return middleNull;
}
public void setMiddleNull(String middleNull) {
this.middleNull = middleNull;
}
public String getLastNull() {
return lastNull;
}
public void setLastNull(String lastNull) {
this.lastNull = lastNull;
}
public String getOverridedName() {
return overridedName;
}
public void setOverridedName(String overridedName) {
this.overridedName = overridedName;
}
public String getValidatedName() {
return validatedName;
}
public void setValidatedName(String validatedName) {
this.validatedName = validatedName;
}
}
Part Two > Extra Classes
1 - Emoji.java
- This a enum class which to demostrate enumeration mapping.
package ray;
public enum Emoji{
SMILE, SAD, CRY, SURPTISE
}
2 - Address.java
- This is embeddable class which to demonstrate reference mapping
package ray;
import javax.persistence.Embeddable;
import javax.persistence.Column;
import javax.validation.constraints.NotNull;
@Embeddable
public class Address{
@NotNull
@Column(nullable = false)
protected String contry;
@NotNull
@Column(nullable = false)
protected String province;
/*
* BUG HERE :
* Hibernate could not recognize @NotNull in embeddable componnet when generate SQL schema,
* But @NotNull can be recognized at runtime for Bean Validation.
* If you want to make sure column is not null,
* Using @Column(nullable = false) instead.
*/
public void setContry(String contry){
this.contry = contry;
}
public String getContry(){
return contry;
}
public void setProvince(String province){
this.province = province;
}
public String getProvince(){
return province;
}
}
3 - AddressDetail.java
- This class is assuming that how to map third party POJO.
package ray;
public class AddressDetail{
protected String city;
protected String town;
/*
* ASSUMING HERE :
* We assume class Address is a third party class,
* There is no annotations here.
* Just a normal POJO.
*/
public void setCity(String city){
this.city = city;
}
public String getCity(){
return city;
}
public void setTown(String town){
this.town = town;
}
public String getTown(){
return town;
}
}
4 - BigName.java
- This class is aimed to simulate convertering entity reference type into field, but it does not work.
/*
* Attribute : converted class
*
* Function : take a transfer before stored into DB
*
* Author : Ray LEE
*
* Date : 2018.09.27
*/
package ray;
import java.io.Serializable;
//If you want transfer a class, take it with interface Serializable
public class BigName implements Serializable{
protected final String nickname;
protected final String formalname;
public BigName(String nickname, String formalname){
this.nickname = nickname;
this.formalname = formalname;
}
public String getNickname(){
return nickname;
}
public String getFormalname(){
return formalname;
}
//You should override method equals & hashCode to compare two instance
public boolean equals(Object o){
if(this == o)
return true;
if(!(o instanceof BigName))
return false;
final BigName bigName = (BigName) o;
if(!nickname.equals(bigName.getNickname()))
return false;
if(!formalname.equals(bigName.getFormalname()))
return false;
return true;
}
public int hashCode(){
int result;
result = nickname.hashCode();
result = 29 * result + formalname.hashCode();
return result;
}
//Methods to help to compelete transfer
public String toString(){
return getNickname() + " " + getFormalname();
}
public static BigName fromString(String s){
String[] str = s.split(" ");
return new BigName(str[0], str[1]);
}
}
5 - BigNameConverter.java
- This class is used to convert class into normal field, does not work too.
/*
* Attribute : converter class
*
* Function : complete the logic commission of transfer BigName
*
* Author : Ray LEE
*
* Date : 2018.09.27
*/
package ray;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
@Converter(autoApply = true)
public class BigNameConverter implements AttributeConverter<BigName, String>{
@Override
public String convertToDatabaseColumn(BigName name){
return name.toString();
}
@Override
public BigName convertToEntityAttribute(String name){
return BigName.fromString(name);
}
}
Part Three > Junit Test
EntityValueTest.java
package ray;
import java.util.List;
import org.junit.Before;
import org.junit.After;
import org.junit.Test;
import org.hibernate.cfg.Configuration;
import org.hibernate.SessionFactory;
import org.hibernate.Session;
import org.hibernate.Transaction;
public class EntityValueTest{
private Configuration cfg;
private SessionFactory sf;
private Session session;
private Transaction tx;
@Before
public void init(){
cfg = new Configuration().configure();
sf = cfg.buildSessionFactory();
session = sf.openSession();
tx = session.beginTransaction();
}
@Test
public void testAppendAload(){
EntityValue ev = new EntityValue();
ev.setFirstNull("first");
ev.setMiddleNull("middle");
ev.setLastNull("last");
ev.setOverridedName("Override");
ev.setValidatedName("Val");
ev.setMetricWeight((double)19.97);
Address address = new Address();
address.setContry("CHIAN");
address.setProvince("HENAN");
AddressDetail ad = new AddressDetail();
ad.setCity("KAIFENG");
ad.setTown("QINGMING");
ev.setAddress(address);
ev.setAddressDetail(ad);
// BigName bigname = new BigName("Abao", "Ray");
// ev.setBigName(bigname);
session.save(ev);
tx.commit();
}
@After
public void destory(){
session.close();
sf.close();
}
}
Part Four > Extra Validation POM Dependency
- import javax.validation.constraints.NotNull;
- import javax.validation.constraints.Size;
dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>