之前一直在用EJB3Q但现在惛_J2SE里面使用JPAQ因Z大喜ƢHibernate的XML文g配置?/p>
而用DB4O的时候,出现了一些莫名其妙的问题Q一直没解决Q就暂时搁下了?/p>
使用myeclipse 6开发jpa如下Q?/p>
1.创徏web project
2.djpa capabilitiesQ选择toplinkQ把自动创徏数据库的和更新persistence.xml的勾?/p>
3.切换成database explorer视图Q选择表,jpa逆向工程?/p>
4.试
生成的persistence.xml如下Q?/p>
<?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0"> <persistence-unit name="JPAWebPU" transaction-type="RESOURCE_LOCAL"> <provider> oracle.toplink.essentials.PersistenceProvider </provider> <class>com.persia.entity.Consumer</class> <properties> <property name="toplink.jdbc.driver" value="com.mysql.jdbc.Driver" /> <property name="toplink.jdbc.url" value="jdbc:mysql://localhost:3306/ejb3" /> <property name="toplink.jdbc.user" value="root" /> <property name="toplink.jdbc.password" value="root" /> <property name="toplink.ddl-generation" value="create-tables" /> </properties> </persistence-unit> </persistence>
生成的几个类如下Q?/p>
实体c:
package com.persia.entity; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; /** * Consumer entity. * * @author MyEclipse Persistence Tools */ @Entity @Table(name = "consumer", catalog = "ejb3", uniqueConstraints = {}) public class Consumer implements java.io.Serializable { // Fields private Integer id; private String name; private String password; // Constructors /** default constructor */ public Consumer() { } /** full constructor */ public Consumer(Integer id, String name, String password) { this.id = id; this.name = name; this.password = password; } // Property accessors @Id @Column(name = "id", unique = true, nullable = false, insertable = true, updatable = true) public Integer getId() { return this.id; } public void setId(Integer id) { this.id = id; } @Column(name = "name", unique = false, nullable = false, insertable = true, updatable = true, length = 45) public String getName() { return this.name; } public void setName(String name) { this.name = name; } @Column(name = "password", unique = false, nullable = false, insertable = true, updatable = true, length = 45) public String getPassword() { return this.password; } public void setPassword(String password) { this.password = password; } }
DAO接口
package com.persia.entity; import java.util.List; /** * Interface for ConsumerDAO. * * @author MyEclipse Persistence Tools */ public interface IConsumerDAO { /** * Perform an initial save of a previously unsaved Consumer entity. All * subsequent persist actions of this entity should use the #update() * method. This operation must be performed within the a database * transaction context for the entity's data to be permanently saved to the * persistence store, i.e., database. This method uses the * {@link javax.persistence.EntityManager#persist(Object) EntityManager#persist} * operation. * * <pre> * EntityManagerHelper.beginTransaction(); * IConsumerDAO.save(entity); * EntityManagerHelper.commit(); * </pre> * * @param entity * Consumer entity to persist * @throws RuntimeException * when the operation fails */ public void save(Consumer entity); /** * Delete a persistent Consumer entity. This operation must be performed * within the a database transaction context for the entity's data to be * permanently deleted from the persistence store, i.e., database. This * method uses the * {@link javax.persistence.EntityManager#remove(Object) EntityManager#delete} * operation. * * <pre> * EntityManagerHelper.beginTransaction(); * IConsumerDAO.delete(entity); * EntityManagerHelper.commit(); * entity = null; * </pre> * * @param entity * Consumer entity to delete * @throws RuntimeException * when the operation fails */ public void delete(Consumer entity); /** * Persist a previously saved Consumer entity and return it or a copy of it * to the sender. A copy of the Consumer entity parameter is returned when * the JPA persistence mechanism has not previously been tracking the * updated entity. This operation must be performed within the a database * transaction context for the entity's data to be permanently saved to the * persistence store, i.e., database. This method uses the * {@link javax.persistence.EntityManager#merge(Object) EntityManager#merge} * operation. * * <pre> * EntityManagerHelper.beginTransaction(); * entity = IConsumerDAO.update(entity); * EntityManagerHelper.commit(); * </pre> * * @param entity * Consumer entity to update * @returns Consumer the persisted Consumer entity instance, may not be the * same * @throws RuntimeException * if the operation fails */ public Consumer update(Consumer entity); public Consumer findById(Integer id); /** * Find all Consumer entities with a specific property value. * * @param propertyName * the name of the Consumer property to query * @param value * the property value to match * @return List<Consumer> found by query */ public List<Consumer> findByProperty(String propertyName, Object value); public List<Consumer> findByName(Object name); public List<Consumer> findByPassword(Object password); /** * Find all Consumer entities. * * @return List<Consumer> all Consumer entities */ public List<Consumer> findAll(); }
DAOc?/p>
package com.persia.entity; import java.util.List; import java.util.logging.Level; import javax.persistence.EntityManager; import javax.persistence.Query; /** * A data access object (DAO) providing persistence and search support for * Consumer entities. Transaction control of the save(), update() and delete() * operations must be handled externally by senders of these methods or must be * manually added to each of these methods for data to be persisted to the JPA * datastore. * * @see com.persia.entity.Consumer * @author MyEclipse Persistence Tools */ public class ConsumerDAO implements IConsumerDAO { // property constants public static final String NAME = "name"; public static final String PASSWORD = "password"; private EntityManager getEntityManager() { return EntityManagerHelper.getEntityManager(); } /** * Perform an initial save of a previously unsaved Consumer entity. All * subsequent persist actions of this entity should use the #update() * method. This operation must be performed within the a database * transaction context for the entity's data to be permanently saved to the * persistence store, i.e., database. This method uses the * {@link javax.persistence.EntityManager#persist(Object) EntityManager#persist} * operation. * * <pre> * EntityManagerHelper.beginTransaction(); * ConsumerDAO.save(entity); * EntityManagerHelper.commit(); * </pre> * * @param entity * Consumer entity to persist * @throws RuntimeException * when the operation fails */ public void save(Consumer entity) { EntityManagerHelper.log("saving Consumer instance", Level.INFO, null); try { getEntityManager().persist(entity); EntityManagerHelper.log("save successful", Level.INFO, null); } catch (RuntimeException re) { EntityManagerHelper.log("save failed", Level.SEVERE, re); throw re; } } /** * Delete a persistent Consumer entity. This operation must be performed * within the a database transaction context for the entity's data to be * permanently deleted from the persistence store, i.e., database. This * method uses the * {@link javax.persistence.EntityManager#remove(Object) EntityManager#delete} * operation. * * <pre> * EntityManagerHelper.beginTransaction(); * ConsumerDAO.delete(entity); * EntityManagerHelper.commit(); * entity = null; * </pre> * * @param entity * Consumer entity to delete * @throws RuntimeException * when the operation fails */ public void delete(Consumer entity) { EntityManagerHelper.log("deleting Consumer instance", Level.INFO, null); try { entity = getEntityManager().getReference(Consumer.class, entity.getId()); getEntityManager().remove(entity); EntityManagerHelper.log("delete successful", Level.INFO, null); } catch (RuntimeException re) { EntityManagerHelper.log("delete failed", Level.SEVERE, re); throw re; } } /** * Persist a previously saved Consumer entity and return it or a copy of it * to the sender. A copy of the Consumer entity parameter is returned when * the JPA persistence mechanism has not previously been tracking the * updated entity. This operation must be performed within the a database * transaction context for the entity's data to be permanently saved to the * persistence store, i.e., database. This method uses the * {@link javax.persistence.EntityManager#merge(Object) EntityManager#merge} * operation. * * <pre> * EntityManagerHelper.beginTransaction(); * entity = ConsumerDAO.update(entity); * EntityManagerHelper.commit(); * </pre> * * @param entity * Consumer entity to update * @returns Consumer the persisted Consumer entity instance, may not be the * same * @throws RuntimeException * if the operation fails */ public Consumer update(Consumer entity) { EntityManagerHelper.log("updating Consumer instance", Level.INFO, null); try { Consumer result = getEntityManager().merge(entity); EntityManagerHelper.log("update successful", Level.INFO, null); return result; } catch (RuntimeException re) { EntityManagerHelper.log("update failed", Level.SEVERE, re); throw re; } } public Consumer findById(Integer id) { EntityManagerHelper.log("finding Consumer instance with id: " + id, Level.INFO, null); try { Consumer instance = getEntityManager().find(Consumer.class, id); return instance; } catch (RuntimeException re) { EntityManagerHelper.log("find failed", Level.SEVERE, re); throw re; } } /** * Find all Consumer entities with a specific property value. * * @param propertyName * the name of the Consumer property to query * @param value * the property value to match * @return List<Consumer> found by query */ @SuppressWarnings("unchecked") public List<Consumer> findByProperty(String propertyName, final Object value) { EntityManagerHelper.log("finding Consumer instance with property: " + propertyName + ", value: " + value, Level.INFO, null); try { final String queryString = "select model from Consumer model where model." + propertyName + "= :propertyValue"; Query query = getEntityManager().createQuery(queryString); query.setParameter("propertyValue", value); return query.getResultList(); } catch (RuntimeException re) { EntityManagerHelper.log("find by property name failed", Level.SEVERE, re); throw re; } } public List<Consumer> findByName(Object name) { return findByProperty(NAME, name); } public List<Consumer> findByPassword(Object password) { return findByProperty(PASSWORD, password); } /** * Find all Consumer entities. * * @return List<Consumer> all Consumer entities */ @SuppressWarnings("unchecked") public List<Consumer> findAll() { EntityManagerHelper.log("finding all Consumer instances", Level.INFO, null); try { final String queryString = "select model from Consumer model"; Query query = getEntityManager().createQuery(queryString); return query.getResultList(); } catch (RuntimeException re) { EntityManagerHelper.log("find all failed", Level.SEVERE, re); throw re; } } }
q有一个非常有用的helper
package com.persia.entity; import java.util.logging.Level; import java.util.logging.Logger; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import javax.persistence.Query; /** * @author MyEclipse Persistence Tools */ public class EntityManagerHelper { private static final EntityManagerFactory emf; private static final ThreadLocal<EntityManager> threadLocal; private static final Logger logger; static { emf = Persistence.createEntityManagerFactory("JPAWebPU"); threadLocal = new ThreadLocal<EntityManager>(); logger = Logger.getLogger("JPAWebPU"); logger.setLevel(Level.ALL); } public static EntityManager getEntityManager() { EntityManager manager = threadLocal.get(); if (manager == null || !manager.isOpen()) { manager = emf.createEntityManager(); threadLocal.set(manager); } return manager; } public static void closeEntityManager() { EntityManager em = threadLocal.get(); threadLocal.set(null); if (em != null) em.close(); } public static void beginTransaction() { getEntityManager().getTransaction().begin(); } public static void commit() { getEntityManager().getTransaction().commit(); } public static void rollback() { getEntityManager().getTransaction().rollback(); } public static Query createQuery(String query) { return getEntityManager().createQuery(query); } public static void log(String info, Level level, Throwable ex) { logger.log(level, info, ex); } }
然后使用helper和daoq行试
package com.jpa.test; import java.util.List; import com.persia.entity.Consumer; import com.persia.entity.ConsumerDAO; import com.persia.entity.EntityManagerHelper; public class JPATest { /** * @param args */ public static void main(String[] args) { // 1.创徏 DAO ConsumerDAO dao = new ConsumerDAO(); // 2.创徏实体c?/span> Consumer c = new Consumer(); c.setName("jpa01"); c.setPassword("jianchi"); // 开始事? EntityManagerHelper.beginTransaction(); // 3. 保存 dao.save(c); // 提交事务真正保存实体到数据库 EntityManagerHelper.commit(); // 4. 列出数据库中所有对?/span> List<Consumer> result = dao.findAll(); for(Consumer o : result) { System.out.println(o.getId()); System.out.println(o.getName()); } } }
试实例如下Q?/p>
package test; import java.util.List; import dao.*; public class JPATest { public static void main(String[] args) { // 1.创徏 DAO StudentDAO dao = new StudentDAO(); // 2.创徏实体c?/span> Student user = new Student(); user.setUsername("hellojpa test"); user.setPassword("jpa password"); user.setAge(20); // 开始事? EntityManagerHelper.beginTransaction(); // 3. 保存 dao.save(user); // 提交事务真正保存实体到数据库 EntityManagerHelper.commit(); // 4. 列出数据库中所有对?/span> List<Student> result = dao.findAll(); for(Student o : result) { System.out.println(o.getId()); System.out.println(o.getUsername()); } // 5. 更改用户?/span> user.setUsername("试JPA"); // 开始事? EntityManagerHelper.beginTransaction(); // 保存QJPA会自动更新变动过的字D) dao.update(user); // 提交事务真正保存实体到数据库 EntityManagerHelper.commit(); // 6. 查找所有年龄ؓ20的用P从第1个开始获取(W?个是W一条记录) result = dao.findByAge(20, 1); for(Student o : result) { System.out.println(o.getId()); System.out.println(o.getUsername()); } // 7. Ҏ ID 查找 user = dao.findById(2); System.out.println(user.getUsername()); // 8. 删除 // 开始事? EntityManagerHelper.beginTransaction(); // 保存QJPA会自动更新变动过的字D) dao.delete(user); // 提交事务真正保存实体到数据库 EntityManagerHelper.commit(); } }
若部|到tomcatQ没有什么问题,也不要额外添加什么?/p>
Table用来定义entity主表的nameQcatalogQschema{属性?
元数据属性说明:
@Entity
@Table(name="CUST")
public class Customer { ... }
一个entity class可以映射到多表,SecondaryTable用来定义单个从表的名字,主键名字{属性?
元数据属性说明:
下面的代码说明CustomercL到两个表,主表名是CUSTOMERQ从表名是CUST_DETAILQ从表的主键列和主表的主键列cd相同Q列名ؓCUST_ID?
@Entity
@Table(name="CUSTOMER")
@SecondaryTable(name="CUST_DETAIL",pkJoin=@PrimaryKeyJoinColumn(name="CUST_ID"))
public class Customer { ... }
当一个entity class映射C个主表和多个从表Ӟ用SecondaryTables来定义各个从表的属性?
元数据属性说明:
@Table(name = "CUSTOMER")
@SecondaryTables( value = {
@SecondaryTable(name = "CUST_NAME", pkJoin = { @PrimaryKeyJoinColumn(name = "STMO_ID", referencedColumnName = "id") }),
@SecondaryTable(name = "CUST_ADDRESS", pkJoin = { @PrimaryKeyJoinColumn(name = "STMO_ID", referencedColumnName = "id") }) })
public class Customer {}
UniqueConstraint定义在Table或SecondaryTable元数据里Q用来指定徏表时需要徏唯一U束的列?
元数据属性说明:
@Entity
@Table(name="EMPLOYEE",
uniqueConstraints={@UniqueConstraint(columnNames={"EMP_ID", "EMP_NAME"})}
)
public class Employee { ... }
Column元数据定义了映射到数据库的列的所有属性:列名Q是否唯一Q是否允ؓI,是否允许更新{?
元数据属性说明:
public class Person {
@Column(name = "PERSONNAME", unique = true, nullable = false, updatable = true)
private String name;
@Column(name = "PHOTO", columnDefinition = "BLOB NOT NULL", secondaryTable="PER_PHOTO")
private byte[] picture;
如果在entity class的field上定义了关系Qone2one或one2many{)Q我们通过JoinColumn来定义关pȝ属性。JoinColumn的大部分属性和ColumncM?
元数据属性说明:
下面的代码说明Custom和Order是一对一关系。在Order对应的映表Z个名为CUST_ID的列Q该列作为外键指向Custom对应表中名ؓID的列?
public class Custom {
@OneToOne
@JoinColumn(
name="CUST_ID", referencedColumnName="ID", unique=true, nullable=true, updatable=true)
public order getOrder() {
return order;
}
如果在entity class的field上定义了关系Qone2one或one2many{)Qƈ且关pd在多个JoinColumnQ用JoinColumns定义多个JoinColumn的属性?
元数据属性说明:
下面的代码说明Custom和Order是一对一关系。在Order对应的映表Z列,一列名为CUST_IDQ该列作为外键指向Custom对应表中名ؓID的列,另一列名为CUST_NAMEQ该列作为外键指向Custom对应表中名ؓNAME的列?
public class Custom {
@OneToOne
@JoinColumns({
@JoinColumn(name="CUST_ID", referencedColumnName="ID"),
@JoinColumn(name="CUST_NAME", referencedColumnName="NAME")
})
public order getOrder() {
return order;
}
声明当前field为映表中的主键列。id值的获取方式有五U:TABLE, SEQUENCE, IDENTITY, AUTO, NONE。Oracle和DB2支持SEQUENCEQSQL Server和Sybase支持IDENTITY,mysql支持AUTO。所有的数据库都可以指定为AUTOQ我们会Ҏ不同数据库做转换。NONE (默认)需要用戯己指定Id的倹{元数据属性说明:
下面的代码声明Task的主键列id是自动增长的?Oracle和DB2从默认的SEQUENCE取|SQL Server和Sybase该列建成IDENTITYQmysql该列建成auto increment?
@Entity
@Table(name = "OTASK")
public class Task {
@Id(generate = GeneratorType.AUTO)
public Integer getId() {
return id;
}
}
当entity class使用复合主键Ӟ需要定义一个类作ؓid class。id class必须W合以下要求:cdd明ؓpublicQƈ提供一个声明ؓpublic的空构造函数。必d现Serializable接,覆写 equals()和hashCodeQ)Ҏ。entity class的所有id field在id class都要定义Q且cd一栗?
元数据属性说明:
public class EmployeePK implements java.io.Serializable{
String empName;
Integer empAge;
public EmployeePK(){}
public boolean equals(Object obj){ ......}
public int hashCode(){......}
}
@IdClass(value=com.acme.EmployeePK.class)
@Entity(access=FIELD)
public class Employee {
@Id String empName;
@Id Integer empAge;
}
在一对多Q多对多关系中,我们可以用Map来保存集合对象。默认用主键值做keyQ如果用复合主键,则用id class的实例做keyQ如果指定了name属性,q指定的field的值做key?
元数据属性说明:
下面的代码说明Person和Book之间是一对多关系。Person的books字段是MapcdQ用Book的isbn字段的g为Map的key?
@Table(name = "PERSON")
public class Person {
@OneToMany(targetEntity = Book.class, cascade = CascadeType.ALL, mappedBy = "person")
@MapKey(name = "isbn")
private Map books = new HashMap();
}
在一对多Q多对多关系中,有时我们希望从数据库加蝲出来的集合对象是按一定方式排序的Q这可以通过OrderBy来实玎ͼ默认是按对象的主键升序排列?
元数据属性说明:
下面的代码说明Person和Book之间是一对多关系。集合books按照Book的isbn升序Qname降序排列?
@Table(name = "MAPKEY_PERSON")
public class Person {
@OneToMany(targetEntity = Book.class, cascade = CascadeType.ALL, mappedBy = "person")
@OrderBy(name = "isbn ASC, name DESC")
private List books = new ArrayList();
}
在三U情况下会用到PrimaryKeyJoinColumn?
元数据属性说明:
下面的代码说明Customer映射C个表Q主表CUSTOMER,从表CUST_DETAILQ从表需要徏立主键列CUST_IDQ该列和主表的主键列id除了列名不同Q其他定义一栗?
@Entity
@Table(name="CUSTOMER")
@SecondaryTable(name="CUST_DETAIL",pkJoin=@PrimaryKeyJoinColumn(name="CUST_ID"QreferencedColumnName="id"))
public class Customer {
@Id(generate = GeneratorType.AUTO)
public Integer getId() {
return id;
}
}
下面的代码说明Employee和EmployeeInfo是一对一关系QEmployee的主键列id作ؓ外键指向EmployeeInfo的主键列INFO_ID?
@Table(name = "Employee")
public class Employee {
@OneToOne
@PrimaryKeyJoinColumn(name = "id", referencedColumnName="INFO_ID")
EmployeeInfo info;
}
如果entity class使用了复合主键,指定单个PrimaryKeyJoinColumn不能满要求Ӟ可以用PrimaryKeyJoinColumns来定义多个PrimaryKeyJoinColumn?
元数据属性说明:
下面的代码说明了Employee和EmployeeInfo是一对一关系。他们都使用复合主键Q徏表时需要在Employee表徏立一个外键,从Employee的主键列id,name指向EmployeeInfo的主键列INFO_ID和INFO_NAME.
@Entity
@IdClass(EmpPK.class)
@Table(name = "EMPLOYEE")
public class Employee {
private int id;
private String name;
private String address;
@OneToOne(cascade = CascadeType.ALL)
@PrimaryKeyJoinColumns({
@PrimaryKeyJoinColumn(name="id", referencedColumnName="INFO_ID"),
@PrimaryKeyJoinColumn(name="name" , referencedColumnName="INFO_NAME")})
EmployeeInfo info;
}
@Entity
@IdClass(EmpPK.class)
@Table(name = "EMPLOYEE_INFO")
public class EmployeeInfo {
@Id
@Column(name = "INFO_ID")
private int id;
@Id
@Column(name = "INFO_NAME")
private String name;
}
Transient用来注释entity的属性,指定的这些属性不会被持久化,也不会ؓq些属性徏表?
@Transient
private String name;
Version指定实体cd乐观事务中的version属性。在实体c重新由EntityManager理q且加入C观事务中Ӟ保证完整性。每一个类只能有一个属性被指定为versionQversion属性应该映到实体cȝ主表上?
下面的代码说明versionNum属性作个类的versionQ映到数据库中主表的列名是OPTLOCK?
@Version
@Column("OPTLOCK")
protected int getVersionNum() { return versionNum; }
Lob指定一个属性作为数据库支持的大对象cd在数据库中存储。用LobTypeq个枚D来定义Lob是二q制cdq是字符cd?
LobType枚Dcd说明Q?
元数据属性说明:
下面的代码定义了一个BLOBcd的属性和一个CLOBcd的属性?
@Lob
@Column(name="PHOTO" columnDefinition="BLOB NOT NULL")
protected JPEGImage picture;
@Lob(fetch=EAGER, type=CLOB)
@Column(name="REPORT")
protected String report;
JoinTable在many-to-many关系的所有者一边定义。如果没有定义JoinTableQ用JoinTable的默认倹{?
元数据属性说明:
下面的代码定义了一个连接表CUST和PHONE的join table。join table的表名是CUST_PHONEQ包含两个外键,一个外键是CUST_IDQ指向表CUST的主键IDQ另一个外键是PHONE_IDQ指向表PHONE的主键ID?
@JoinTable(
table=@Table(name=CUST_PHONE),
joinColumns=@JoinColumn(name="CUST_ID", referencedColumnName="ID"),
inverseJoinColumns=@JoinColumn(name="PHONE_ID", referencedColumnName="ID")
)
TableGenerator定义一个主键值生成器Q在Idq个元数据的generateQTABLEӞgenerator属性中可以使用生成器的名字。生成器可以在类、方法或者属性上定义?
生成器是为多个实体类提供q箋的ID值的表,每一行ؓ一个类提供ID|ID值通常是整数?
元数据属性说明:
下面的代码定义了两个生成器empGen和addressGenQ生成器的表是ID_GEN?
@Entity public class Employee {
...
@TableGenerator(name="empGen",
table=@Table(name="ID_GEN"),
pkColumnName="GEN_KEY",
valueColumnName="GEN_VALUE",
pkColumnValue="EMP_ID",
allocationSize=1)
@Id(generate=TABLE, generator="empGen")
public int id;
...
}
@Entity public class Address {
...
@TableGenerator(name="addressGen",
table=@Table(name="ID_GEN"),
pkColumnValue="ADDR_ID")
@Id(generate=TABLE, generator="addressGen")
public int id;
...
}
SequenceGenerator定义一个主键值生成器Q在Idq个元数据的generator属性中可以使用生成器的名字。生成器可以在类、方法或者属性上定义。生成器是数据库支持的sequence对象?
元数据属性说明:
下面的代码定义了一个用提供商默认名称的sequence生成器?
@SequenceGenerator(name="EMP_SEQ", allocationSize=25)
DiscriminatorColumn定义在用SINGLE_TABLE或JOINEDl承{略的表中区别不l承层次的列?
元数据属性说明:
下面的代码定义了一个列名ؓDISCQ长度ؓ20的Stringcd的区别列?
@Entity
@Table(name="CUST")
@Inheritance(strategy=SINGLE_TABLE,
discriminatorType=STRING,
discriminatorValue="CUSTOMER")
@DiscriminatorColumn(name="DISC", length=20)
public class Customer { ... }