Understanding of TABLE_PER_CLASS with keys in eclipselink - java

I have a simple Java EE 7 Web App with Eclipselink and the TABLE_PER_CLASS inheritance strategy.
Following classes:
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
#Entity
public abstract class AbstractService implements Serializable {
private static final long serialVersionUID = 7041207658121699813L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne
#JoinColumn
private PersonGroup personGroup;
}
#Entity
public class Service extends AbstractService implements Serializable {
private static final long serialVersionUID = 3817106074951672799L;
}
#Entity
public class PersonGroup implements Serializable {
private static final long serialVersionUID = 3205092801888510996L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "personGroup")
private List<AbstractService> services;
}
In persistence.xml I do Drop&Create.
After creating the database, I have this tables:
abstractservice, service, persongroup
The point is now, that eclipselink creates the table abstractservice with (only(!)) the attribute persongroup_id (no "id" attribute). Why?
My understanding from TABLE_PER_CLASS is, that every attribute and key is going "down", so abstractservice should have no more attributes and should not exist.
My businesscase is, that I have a lot of subservice from AbstractService. I want to get all subservices from AbstractService with a special persongroup.
The AbstractServicetable has no entries, because everything is in Service.
With CriteriaBuilder I say:
Select from AbstractService where persongroup_id = 123;
The Criteria Api should build this (with some union, if more subservices would exist), because I have TABLE_PER_CLASS:
Select from Service where persongroup_id = 123;
Why is eclipselink creating persongroup_id in abstractService and how can I solve my case?
At the end the result of the query is always empty, because abstractService is empty...

Same question was asked here: http://www.eclipse.org/forums/index.php/t/406338/
and seems to be related to bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=265702 which was fixed but regressed. A new bug should be filed for it if you are seeing this in the latest version.
If you are only using a single Servide subclass, you might want to make it a mappedSuperclass instead. If not, a different inheritance type such as joined or single table is usually recommended. This bug seems to only affect DDL generation, so you can switch to have JPA create a script that you can then edit to remove the AbstractService table entries.

Related

Complex select on EAV model Spring data jpa

I have a Product Entity like below (It's simple version)
#Entity
#Table(name = "product")
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#OneToMany(mappedBy = "product")
private List<ProductAtt> attributes;
}
Each Product could have one or more Attribute. Attribute look likes below
#Entity
#Table(name = "attribute")
public class Attribute {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String name;
}
So I create a relation entity like below with extra value property
#Entity
#Table(name = "product_att")
public class ProductAtt implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#ManyToOne
#JoinColumn
private Product product;
#ManyToOne
#JoinColumn
private Attribute attribute;
private int value;
}
Now I want to find all products that have some attributes with custom values. For example all products that have attribute 1 with value 3 and attribute 2 with value 40 and ... .
What is the simplest and most efficient query to do that?
Since the number of attributes to query is not known at design time, one of the dynamic query mechanisms supported by Spring Data JPA will have to be used. The query can certainly be built using the JPA Specification or Criteria APIs.
If using QueryDSL support, subqueries with exists can be used. The following example shows how this can be done (assuming Java 8 and QueryDSL 4).
interface ProductRepository
extends CrudRepository<Product, Long>
, QueryDslPredicateExecutor<Product> {
default Iterable<Product> findAllByAttributes(final Map<String, String> attributes) {
final QProduct root = QProduct.product;
BooleanExpression query = root.isNotNull();
for (final String attribute : attributes.keySet()) {
final QProductAttribute branch = root.attributes.any();
final BooleanExpression subquery = branch.attribute.name.equalsIgnoreCase(attribute)
.and(branch.value.equalsIgnoreCase(attributes.get(attribute)));
query = query.and(JPAExpressions.selectFrom(QProductAttribute.productAttribute).where(subquery).exists());
}
return findAll(query);
}
}
It should be noted that the database design is such that performance problems are bound to happen, because the same table (ProductAttr) is included as many times as there are attributes to search by. This is not a problem of QueryDSL, JPA, Hibernate, SQL or the database server but the data model itself (also known as the EAV model).

Jpa hibernate and Table_Per_class inheritance

I have an entity that is super class
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
#Table(name = "super_class")
public abstract class SuperClass implements Serializable {
#Transient
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
private long id;
public abstract void initDefaultValues();
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
and some subclasses that extend the SuperClass.
#Entity
#Table(name = "Subclass1")
public class Subclass1 extends SuperClass{
private static final Logger log = LogManager
.getLogger(Subclass1.class);
#Transient
private static final long serialVersionUID = 1L;
// testcase configuration tab
private String configurationTabTestServer;
private String umtsRelease;
}
The other classes look the same.
I used to have them SINGLE_TABLE for inheritance type but we wanted each concrete class to have each own table. Because of TABLE_PER_CLASS I had to use GenerationType.TABLE.
I also have an entity class that has a foreign key to the super class
#Entity
#Table(name="myother_entity")
class Entity1{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#OneToOne(cascade = CascadeType.ALL)
private SuperClass superclass;
//more fields
}
I used an abstract class because I have one Entity1 class that could have different type of Superclass. We didn't want to create different Entity1 and Entity2 and Entity3 etc classes for each subclass. So we created one Entity1 class that can have a field of type SuperClass, that could point to any of the subclasses.
On my program I create many Entity1 intances that some of them that have different type of superclass as field value. Each could be of type subclass1 or subclass2 etc. At first we used to have one single table for all subclasses. Everything worked fine. But after we decided to split our tables this is what it happens. When I edit any Entity1 instance, that has already SuperClass field set(using one of the sub classes), and save it (merging it) then it creates a new instance of my Subclass associated with my Entity1 instance, and then saves it to the database. So I have two records now on the table of the subclass. This didn't happen when we used a SINGLE_TABLE inheritance type. Is this normal behaviour for JPA and hibernate?
Please, first consider this: DiscriminatorColumn and DiscriminatorValue annotations are specific to single-table approach. So they aren't to be used in table-per-class mappings.
Now, let's go to the issue:
In table-per-class mapping, there will be two records with same ID: one in a parent table, other in a child table.
As I understood, in your case, two records are being written in the child table, right? If so, the problem must be when you load the Entity1 data from the database. The property "superclass" must have its ID set. You can use eager or lazy loading for this. And check if that property is properly loaded (in debug mode) with its correct ID set before saving it.
Another way is to disable "cascade persist/merge" and to save the entities separately. It can provide more security to your data.
You can find more information here: http://docs.oracle.com/javaee/6/tutorial/doc/bnbqn.html

hibernate, stackoverflow with particular entity mapping

I have the following mapping:
#Entity
public class Satellite implements Serializable, Comparable<Satellite> {
#NotNull #Id
private long id;
.....
#OrderColumn
#OneToMany(mappedBy = "satellite", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<DataModel> dataModel;
}
and a child entity:
#Entity #IdClass(value=DataModelPK.class)
public class DataModel implements Serializable, Comparable<DataModel> {
private static final long serialVersionUID = -3416403014857250990L;
#Id
private int orbit; // related to reference orbit file
private int dataPerOrbit; // in Gbit
#ManyToOne #Id
private Satellite satellite;
}
originally, DataModel was an embeddable entity, but for a better control over the primary key and the underlying structure of the db, I switched to a more traditional model.
The point is, during the loading of the entity now it generate a stack overflow!! I think there is some cyclic loading between those two entities and it got stuck!
I'm thinking to revert everything back to what it was, but I wish to understand why it gives me this error.
You have #IdClass for DataModel specified to be DataModelPK.class but your #Id annotation is on an int field.
This is a problem, it may be causing you stackoverflow but I am not certain.
Update I now see the second #Id annotation so I stand corrected, I will investigate furtuer.

Spring Rest servcie prodouces JSON but not for objects mapped with a db

It took me hours to realize, what the problem is:
I have a Spring Rest service and a GET-Method which returns a user in JSON-Format.
The data comes from my database over sessionFactory.
After debugging it turned out, that the Problem is related to my bidrectional onetomany-manytoone relationship.
So calling
User user = (User) sessionFactory.getCurrentSession().load(User.class, userId);
returns a User-Object where user.getCity().getSupplier() runs into an com.sun.jdi.InvocationException. Therefore Jackson is obviously unable to serialize.
But what causes this exception?
#Entity
#Table(name = "T_CITY")
public class City implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private long id;
#OneToMany(mappedBy = "city", cascade=CascadeType.ALL)
private Set<User> user;
#OneToMany(mappedBy = "city", cascade=CascadeType.ALL)
private Set<Supplier> supplier;
User:
#Entity
#Table(name = "T_USER")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
public User() {
}
#Id
private long id;
#ManyToOne
private City city;
Supplier:
#Entity
#Table(name = "T_SUPPLIER")
public class Supplier implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private long id;
#ManyToOne
private City city;
As mentioned in the other answer, I think you'll find that your issues are related to the x-to-x relationships. This can sometimes create circular reference issues when trying to jsonify the entity beans.
Sometimes you can avoid or get past this by using annotations, other times a wrapper class is needed. I often just write a wrapper class to handle my JSON transactionts
There are many many references to this type of issues spanning many languages. Here a few starting points for you to research.
Google related search regarding circular references in entity objects
Json and Java - Circular Reference
Circular Dependencies With Jackson

why #TableGenerator(name="tabgen1",table="cID",allocationSize=1) create a new table

In my small project i will create 4 table, 2 of this table have One-to-One relations:
#Entity #org.hibernate.annotations.Entity(dynamicUpdate = true) #SuppressWarnings("serial") public class Creation implements Serializable {
#TableGenerator(name="tabgen1",table="cID",allocationSize=1,pkColumnName="ColumnName",pkColumnValue="ColVal",valueColumnName="valCOLName")
#GeneratedValue(strategy= GenerationType.AUTO,generator="tabgen1")
#Id
private Long UserId;
and second Entity is
#Entity #org.hibernate.annotations.Entity(dynamicUpdate = true) public class Worker implements Serializable {
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Creation creation;
#Id
#TableGenerator(name="tabgen2",table="WID",allocationSize=1)
#GeneratedValue(strategy= GenerationType.AUTO,generator="tabgen2")
private Long WorkerId;
and it create more tables...., but i want safe generated walues in one of my 4 tables, its real or i make some mistakes?
I'm not sure I understand the question, but specifying AUTO and generator at the same time is illogical. AUTO means that the ORM should pick the generation strategy it finds the most appropriate. And generator forces it to use a specific generator. So they're contradictory.
Use TABLE instead of AUTO, since you want the ID to be generated using a table generator.

Categories

Resources