JPWH :Mappping entity & value type with 5 unresolved problems

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>
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值