EclipseLink + JPA + Generic Entity + SINGLE_TABLE Inheritance - java

I was wondering if it's possible in JPA to define a generic entity like in my case PropertyBase and derive concrete entity classes like ShortProperty and StringProperty and use them with the SINGLE_TABLE inheritance mode?
If I try to commit newly created ElementModel instances (see ElementModelTest) over the EntityManager I always get an NumberFormatException that "value" can't be properly converted to a Short. Strangely enough if I define all classes below as inner static classes of my test case class "ElementModelTest" this seems to work.
Any ideas what I need to change to make this work?
I'm using EclipseLink eclipselink-2.6.0.v20131019-ef98e5d.
public abstract class PersistableObject implements Serializable {
private String id = UUID.randomUUID().toString();
private Long version;
}
public abstract class PropertyBase<T> extends PersistableObject {
private String name;
private T value;
}
public class ShortProperty extends PropertyBase<Short> {
...
}
public class StringProperty extends PropertyBase<String> {
...
}
public class ElementModel extends PersistableObject {
private StringProperty name = new StringProperty();
private ShortProperty number = new ShortProperty();
}
public class ElementModelTest extends ModelTest<ElementModel> {
#Test
#SuppressWarnings("unchecked")
public void testSQLPersistence() {
final String PERSISTENCE_UNIT_NAME = getClass().getPackage().getName();
new File("res/db/test/" + PERSISTENCE_UNIT_NAME + ".sqlite").delete();
EntityManagerFactory factory = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
EntityManager em = factory.createEntityManager();
em.getTransaction().begin();
for (int i = 0; i < 10; ++i) {
ElementModel device = new ElementModel();
device.setName("ElementModel: " + i);
device.setNumber((short) i);
em.persist(device);
}
em.getTransaction().commit();
em.close();
}
}
<?xml version="1.0" encoding="UTF-8" ?>
<entity-mappings version="2.1"
xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence/orm http://xmlns.jcp.org/xml/ns/persistence/orm_2_1.xsd">
<mapped-superclass
class="PersistableObject">
<attributes>
<id name="id">
<column name="id" />
</id>
<version name="version" access="PROPERTY">
<column name="version" />
</version>
</attributes>
</mapped-superclass>
<entity class="PropertyBase">
<table name="PropertyBase" />
<inheritance />
<discriminator-column name="type"/>
<attributes>
<basic name="name">
<column name="name" />
</basic>
<basic name="value">
<column name="value" />
</basic>
</attributes>
</entity>
<entity class="StringProperty">
<discriminator-value>StringProperty</discriminator-value>
</entity>
<entity class="ShortProperty">
<discriminator-value>ShortProperty</discriminator-value>
</entity>
<entity class="ElementModel">
<table name="ElementModel" />
<inheritance />
<discriminator-column name="type"/>
<attributes>
<one-to-one name="name">
<join-column name="name" referenced-column-name="id" />
<cascade>
<cascade-all />
</cascade>
</one-to-one>
<one-to-one name="number">
<join-column name="number" referenced-column-name="id" />
<cascade>
<cascade-all />
</cascade>
</one-to-one>
</attributes>
</entity>
</entity-mappings>

Your problem is that PropertyBase<T> is an entity with field value being persistable. Your database needs to map that field type to a column type, and you have three entities that have different java types for the same field: PropertyBase<T> is generic and has itself no idea what type its value field is, StringProperty says it is a String and ShortProperty says that is a Short. So that is a conflict.
In order to overcome this problem, you make field value transient, and for every subtype of PropertyBase<T> (like StringProperty) you can define a new persitable property with different names, eg.
StringProperty will have a private String stringValue, ShortProperty will have a private Short shortValue, and every field will be mapped to different DB column.
PS: I cannot explain why it works when you make all the classes static inner.

Related

java.lang.Object; cannot be cast to shoppingbasket.Product

I am trying to retrieve a list of products with they're associated offers. After iterating through the result of the query I want to be able to use the getters/setters from the products class but I know it's not working because the query is not returning an instance of Product.
function to grab the products:
public List<Product> getProducts() {
factory = (new Configuration()).configure().buildSessionFactory();
Session session = factory.getCurrentSession();
session.beginTransaction();
List<Product> products = new ArrayList<Product>();
Query query = session.createQuery("from Product p INNER JOIN p.offers");
//The castList is declared and explained at the bottom of listing
List<Product> list = query.list();
Iterator<Product> iter = list.iterator();
while (iter.hasNext()) {
Product product = iter.next();
System.out.println(product);
}
}
Hibernate mapping for Offer:
<hibernate-mapping>
<class name="shoppingbasket.Offer" table="offers">
<id name="offerID" type="integer" access="field">
<column name="OfferID" />
<generator class="assigned" />
</id>
<property name="offerDescription" type="java.lang.String" access="field">
<column name="OfferDescription" length="60" not-null="true"/>
</property>
<property name="shortDescription" type="java.lang.String" access="field">
<column name="ShortDescription" length="10" not-null="false"/>
</property>
<property name="TFTPOTGroup" type="java.lang.Integer" access="field">
<column name="TFTPOTGroup" length="4" not-null="false" default="null"/>
</property>
<property name="discountPercentage" type="java.lang.Double" access="field">
<column name="DiscountPercentage" not-null="false" default="null"/>
</property>
</class>
Hibernate mapping for Product:
<hibernate-mapping>
<class name="shoppingbasket.Product" table="products">
<id name="productID" type="integer" access="field">
<column name="ProductID" />
<generator class="assigned" />
</id>
<property name="offerID" type="java.lang.Integer" access="field">
<column name="OfferID" />
</property>
<property name="productName" type="java.lang.String" access="field">
<column name="ProductName" length="40" not-null="true"/>
</property>
<property name="unitPrice" type="java.math.BigDecimal" access="field">
<column name="UnitPrice"/>
</property>
<one-to-one name="offers" class="shoppingbasket.Offer" />
</class>
Product class:
public class Product implements java.io.Serializable {
private Integer productID;
private Integer offerID;
private String productName;
private BigDecimal unitPrice;
private Offer offer;
public Integer getProductID() {
return productID;
}
public void setProductID(Integer productID) {
this.productID = productID;
}
public Integer getOfferID() {
return this.offerID;
}
public void setOfferID(Integer offerID) {
this.offerID = offerID;
}
public Offer getOffers() {
return offer;
}
public void setOffers(Offer offer) {
this.offer = offer;
}
//more getters/setters
}
Offer class:
public class Offer
{
private Integer offerID;
private String offerDescription;
private String shortDescription;
private Integer TFTPOTGroup;
private Double discountPercentage;
public Integer getOfferID() {
return offerID;
}
public void setOfferID(Integer offerID) {
this.offerID = offerID;
}
//more getters/setters
}
Any help would be hugely appreciated
#Potential Unnecessary Projections Data:
Since you're not specifying SELECT clause in the query and putting explicit joins, hibernate will return 2 objects per row (Product, Offers), wherein Product object might already be populated with the Offer data due to underlying mapping associations.
Try adding select clause to the query and see if it casts correctly
Add SELECT p FROM ... to the query

Hibernate, No identifier specified for entity, For mapping String id

I use :
mysql-connector-java 6.0.6
hibernate 5.2.10.Final
spring 4.3.8.RELEASE
Class code :
public class PhoneNumber extends AbstractValue{
public static final int CELL_PHONE = 1, HOME_PHONE = 2, WORK_PHONE = 3;
public PhoneNumber(String value, Integer type) {
super(value, type);
}
public PhoneNumber() {
this(null,null);
}
}
Parent class :
public abstract class AbstractValue {
private String value;
private Integer type;
public AbstractValue(String value, Integer type) {
this.value = value;
this.type = type;
}
public String getValue() {
return value;
}
public Integer getType() {
return type;
}
public void setValue(String value) {
this.value = value;
}
public void setType(Integer type) {
this.type = type;
}
}
Mapping :
<entity class="PhoneNumber" name="PhoneNumber">
<table name="PhoneNumber"/>
<attributes>
<id name="value">
</id>
<basic name="type">
<column nullable="false"/>
</basic>
</attributes>
</entity>
Already tried :
<entity class="PhoneNumber" name="PhoneNumber">
<table name="PhoneNumber"/>
<attributes>
<id name="value" access="FIELD">
<generated-value strategy="SEQUENCE" generator="IdSeq" />
<sequence-generator name="IdSeq" sequence-name="IdSeq" allocation-size="1" />
</id>
<basic name="type">
<column nullable="false"/>
</basic>
</attributes>
</entity>
<entity class="PhoneNumber" name="PhoneNumber">
<table name="PhoneNumber"/>
<attributes>
<id name="value">
<generated-value strategy="IDENTITY" generator="uuid" />
</id>
<basic name="type">
<column nullable="false"/>
</basic>
</attributes>
</entity>
<entity class="PhoneNumber" name="PhoneNumber">
<table name="PhoneNumber"/>
<attributes>
<id name="value">
<generated-value strategy="TABLE" generator="uuid" />
<table-generator name="uuid" />
</id>
<basic name="type">
<column nullable="false"/>
</basic>
</attributes>
</entity>
And already read : (so i hope i don't do duplicate)
org.hibernate.AnnotationException: No identifier specified for entity: com.ubosque.modelo.Ciudadano
org.hibernate.AnnotationException: No identifier specified for entity: login.Users
java.lang.RuntimeException: org.hibernate.AnnotationException: No identifier specified for entity
Org.Hibernate.AnnotationException: No Identifier Specified For Entity I don't have a id in my table
org.hibernate.AnnotationException: No identifier specified for entity using JPA XML entity-mapping
No Identifier specified exception even when it was
string id generator
How to use #Id with String Type in JPA / Hibernate?
and some more...
error :
Caused by: org.hibernate.AnnotationException: No identifier specified for entity: com.mayan.nst.server.model.PhoneNumber
IF its possible i was prefer a solution were the id will NOT be generated
Thanks you very mutch for read, and for any help
Thanks to Neil Stocktin the problem was that i tried to get superclass property from this child. (not working). solution will be add
<entity class="AbstractValue">
<attributes>
<id name="value">
</id>
</attributes>
</entity>
and remove the from the child
Update
better solution, to separate the super class childrens tables. use :
<mapped-superclass class="AbstractValue">
<attributes>
<id name="value"/>
<basic name="type">
<column nullable="false"/>
</basic>
</attributes>
</mapped-superclass>

Hibrernate: Attribute "type" must be declared for element type "column"

I am trying to save to two tables in SQL server using Hibernate:
ORDER and ORDER_ITEM
I get an error:
Attribute "type" must be declared for element type "column".
Initial SessionFactory creation failed.org.hibernate.InvalidMappingException: Unable to read XML.
This produces a NullPointerException
If I understand correctly, this means that when I try to save to the order_item table, the getter for the foreign key is empty, but how would I set it if is designed to 'Autoincrement', I thought that hibernate would handle this in it's transaction.
Below are my POJO's and Hibernate mappings. I have omitted the getters and setters from this copy/paste, but they are present in my actual code
I am also successful in saving just to the ORDER table if I remove the set
Order.java:
public class Order {
public Order(){
super();
}
private int orderId;
private Set<LineItem> items;
private String strPhone;
private String strEmail;
private String strFirstName;
private String strLastName;
private String strParentFirstName;
private String strParentLastName;
private String strOrganizationName;
private String strOrganizationType;
private String strComment;
}
order.hbm.xml:
<hibernate-mapping>
<class name="dbobjects.Order" table="orders">
<id name="orderId" type="integer" column="order_id">
<generator class="increment"/>
</id>
<property name="strPhone" type="string" column="phone_number"/>
<property name="strFirstName" type="string" column="first_name"/>
<property name="strLastName" type="string" column="last_name"/>
<property name="strParentFirstName" type="string" column="parent_first_name"/>
<property name="strParentLastName" type="string" column="parent_last_name"/>
<property name="strOrganizationName" type="string" column="organization_name"/>
<property name="strOrganizationType" type="string" column="organization_type"/>
<property name="strComment" type="string" column="comments"/>
<set name="items" table="order_item" fetch="select" cascade="all">
<key>
<column name="orderId" type="java.lang.Integer"/>
</key>
<one-to-many class="dbobjects.LineItem"/>
</set>
</class>
LineItem.java:
public class LineItem {
public LineItem(){
super();
}
private int orderItemId;//this will be the primary key
private int orderId;//this is the foreign key to the order
private String age;
private String gender;
private String type;
private String itemSize;
private int itemQuantity;
}
lineItem.hbm.xml:
<id column="order_item_id" name="orderItemId" type="integer">
<generator class="increment"/>
</id>
<property column="age" name="age" type="string"/>
<property column="gender" name="gender" type="string"/>
<property column="quantity" name="itemQuantity" type="integer"/>
<property column="size" name="itemSize" type="string"/>
<property column="clothing_type" name="clothingType" type="string"/>
<many-to-one name="orderId" class="dbobjects.Order" fetch="select" column="order_id" type="java.lang.Integer"/>
This is where the error is thrown when I Instanciate the session:
**session = HibernateUtil.getSessionFactory().openSession();**←
try{
session.beginTransaction();
session.save(order);
session.getTransaction().commit();
So the question is: How do I handle this situation with 'autoincrement' primary key that is not accessible to another table as a foreign key
So, There were a few problems:
In order to submit a 'child' to the database, I needed to show who is the parent.
This means that my LineItem Class needed a
Order order;
property instead of a simple
private String orderID;
this was accomplished after the Order object was ready to be submitted to the DB but before the actual call:
for (LineItem item : order.getItems()) {
item.setOrder(order);
}
I also changed my DTD's from !DOCTYPE hibernate-configuration SYSTEM classpath://org/hibernate/hibernate-mapping-3.0.dtd"
to
!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"
Just add "type" attribute like this :
<column name="orderId" type="java.lang.Integer"/>
Try to replace :
<many-to-one name="orderId" column="order_id" class="dbobjects.Order" cascade="all">
<column name="orderId" type="java.lang.Integer"/>
</many-to-one>
by
<many-to-one name="orderId" column="order_id" class="dbobjects.Order" cascade="all"/>

Hibernate - insert data with one to many relationship

I am trying to insert data into a Patient table, which has a many-to-one relationship with Site. Site has a one-to-many relationship with Patient.
However, I get a org.postgresql.util.PSQLException: ERROR: insert or update on table "patients" violates foreign key constraint "fk_427e3ubwhw8n7a4id3mmrmjgj"
Detail: Key (patient_id)=(31) is not present in table "sites".
I have tried to create a set of patients, add my patient to this set, create a site object, then use this object to set the patients. I am not sure which part of this is going wrong.
Session session = this.getFactory().openSession();
Transaction transaction = null;
try{
transaction = session.beginTransaction();
Date parsedDob = Date.valueOf(dob);
Date parsedDateReg = Date.valueOf(dateReg);
Site site = new Site();
site.setSiteId(1);
IPatient p = new Patient();
p.setFirstName(firstName);
p.setLastName(lastName);
p.setDob(parsedDob);
p.setDateRegistered(parsedDateReg);
p.setSite(site);
Set<IPatient> patientSet = new HashSet<IPatient>();
patientSet.add(p);
site.setPatients(patientSet);
session.save(site);
session.save(p);
transaction.commit();
}catch(Exception e){
e.printStackTrace();
}
My Patient.hbm.xml file is:
<?xml version="1.0"?><!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.example.model">
<class name="Patient" table="patients">
<id name="patientId" column="patient_id">
<generator class="sequence" />
</id>
<version name="version" column="version" />
<property name="firstName" column="first_name" />
<property name="lastName" column="last_name" />
<property name="dob" column="dob" />
<property name="gender" column="gender" />
<property name="dateRegistered" column="date_registered" />
<many-to-one name="site" class="com.example.model.Site" not-null="true" />
<set name="visits" cascade="all">
<key column="visit_id" />
<one-to-many class="Visit" />
</set>
</class>
My Site.hmx.xml is:
<?xml version="1.0"?><!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.example.model">
<class name="Site" table="sites">
<id name="siteId" column="site_id">
<generator class="sequence" />
</id>
<version name="version" column="version" />
<property name="name" column="name" />
<set name="patients" cascade="all">
<key column="patient_id" />
<one-to-many class="Patient" />
</set>
</class>
My Patient.java
public class Patient implements IPatient{
private Integer version;
private Integer patientId;
private Set<IVisit> visits;
private Site site;
private String firstName;
private String lastName;
private Date dob;
private Gender gender;
private Date dateRegistered;
public Patient(){
}
}
My Site.java
public class Site {
private Integer siteId;
private Integer version;
private Set<IPatient> patients;
private String name;
public Site(){
}
}
After the line below you should save your site object to db;
site.setSiteId(1);
session.save(site);
After that you can set site object to patient;
p.setSite(site);

Hibernate mapping for superclass

I have to perform hibernate mapping only by using annotation method in subclass according to following scenario in Spring MVC3 enviroment:
1) I have class called Event.java from xxxx.jar library with following properties
public Class Event{
public integer id;
public String start_date;
public String start_date;
public String text;
}
2) POJO class CustomEvent.java extending Event.java class using inheritance with following structure.
public class CustomEvent extends Event implements Serializable {
private static final long serialVersionUID = 1L;
public String user;
public String type;
public String contact;
public String status;
}
3) Im able to perform hibernate mapping using XML in following way
<hibernate-mapping>
<class name="<package>.model.CustomEvent" table="events">
<id column="event_id" name="id" type="java.lang.Integer">
<generator class="increment"/>
</id>
<property column="start_date" name="start_date" type="timestamp"/>
<property column="end_date" name="end_date" type="timestamp"/>
<property column="text" name="text" type="java.lang.String"/>
<property column="user" name="user" type="java.lang.String"/>
<property column="type" name="type" type="java.lang.String"/>
<property column="contact" name="contact" type="java.lang.String"/>
<property column="status" name="status" type="java.lang.String"/>
</class>
</hibernate-mapping>
and its working fine.
Q) How I can achieve the same thing with JPA/Hibernate annotations?
I am new bay for Hibernate / Spring framework.
Note: Event class is present in .jar library, not able to see the code exactly

Categories

Resources