Creating missing subclass entity when super exists - java

I have 2 tables S and I on the database (with a 1:1 relationship), they both have the same id as pk and the hibernate classes I've created are like these:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public class S {
#Id
#Column(name = "id")
#GeneratedValue(...)
#SequenceGenerator...
private long id;
....
}
#Entity
#PrimaryKeyJoinColumn(name = "id")
public class I extends S {
....
}
Because of historical reasons, in the database there are objects of type S but not the associated objects of type I. I want to create those I type objects using hibernate. How can I do that? Can I create an I type object from an left join HQL query like this?
select i from I i right join i.id s where s.id = :id
If I try to create a new I entity (new I()) and then persist it, I only managed to get some exceptions as it tries to create an already existing S record. I can't do a simple read/load for I entity as I record does not exist yet. How can I do to create this missing I part entity?
PS I will adjust the question if you point me the unclear things

One approach that will certainly work for you (while is isn't clean one) is to create I records with SQL inserts directly: insert into I_table values (...).
When there are corresponding records in I_table, ORM will start load your objects with I type.
If you have to stay with your ORM and you can delete S records then you can
Load S by id
Delete S (flush? based on your flush mode)
Create I
Copy S values into I
Save I

What you're trying to create is an entity hierarchy. So have to map the entities correctly. The following is probably what you need:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(discriminatorType = DiscriminatorType.CHAR)
#DiscriminatorValue("S")
public class S {
#Id
//........
private long id;
....
}
#Entity
#DiscriminatorValue("I")
public class I extends S {
....
}
With this setting the table S will contain a column named DTYPE (for discriminator type) which identifies whether a row belongs to S or I; this is the default; if you don't want that you have to give a name for the DiscriminatorColumn annotation.
Create an instance of S and save
Create an instance of 'I' by populating the inherited properties (i.e., the properties of S) and its own properties, and save.
When you create a query targeting I, you'll get only instances of I, but if your query targets the S, you'll get instances of both entities.

Related

How do I make JPA entity field truly write-only

I have a case where I'm persisting a large jsonb field into a PostGres table, but do not want to read it when I fetch the entity; if I do fetch it, my service goes OOM. A better design might be to separate this into a 1 to 1 table, but I can't do that at this time.
To plead that this is not a duplicate question, here's some of my research:
I'm not able to mark the column LAZY since I have a simple column not a join`
JPA/Hibernate write only field with no read
I tried the empty setter in this suggestion, which makes sense - but it still appears to read the column and I OOM: https://www.zizka.ch/pages/programming/java/hibernate/hibernate-write-only.html
I also tried omitting the setter altogether in my #Data class: Omitting one Setter/Getter in Lombok
So, I can not see the field, but I can't seem to keep it from being read into memory in the background. It seems like there must be some simple setting in JPA or Hibernate to exclude a column from read. Before I go try to make a complex repository hierarchy just to see if it works, I thought I would ask here in case I get lucky.
Thanks in advance!
Lazy loading attributes
Hibernate can load attribute lazily, but you need to enable byte code enhancements:
First you need to set the property hibernate.enhancer.enableLazyInitialization to true
Then you can annotate the field with #Basic( fetch = FetchType.LAZY ).
Here's the example from the documentation:
#Entity
public class Customer {
#Id
private Integer id;
private String name;
#Basic( fetch = FetchType.LAZY )
private UUID accountsPayableXrefId;
#Lob
#Basic( fetch = FetchType.LAZY )
#LazyGroup( "lobs" )
private Blob image;
//Getters and setters are omitted for brevity
}
You can also enable this feature via the Hibernate ORM gradle plugin
Named Native queries
You could also decide to not map it and save/read it with a named native query. It seems a good trade off for a single attribute - it will just require an additional query to save the json.
Example:
#Entity
#Table(name = "MyEntity_table")
#NamedNativeQuery(
name = "write_json",
query = "update MyEntity_table set json_column = :json where id = :id")
#NamedNativeQuery(
name = "read_json",
query = "select json_column from MyEntity_table where id = :id")
class MyEntity {
....
}
Long id = ...
String jsonString = ...
session.createNamedQuery( "write_json" )
.setParameter( "id", id )
.setParameter( "json", jsonString )
.executeUpdate();
jsonString = (String)session.createNamedQuery( "read_json" )
.setParameter( "id", id )
.getSingleResult();
In this case, schema generation is not going to create the column, so you will need to add it manually (not a big deal, considering that there are better tools to update the schema in production).
MappedSuperclass
You can also have two entities extending the same superclass (this way you don't have to copy the attributes). They have to update the same table:
#MappedSuperclass
class MyEntity {
#Id
Long id;
String name
...
}
#Entity
#Table(name = "MyEntity_table")
class MyEntityWriter extends MyEntity {
String json
}
#Entity
#Table(name = "MyEntity_table")
class MyEntityReader extends MyEntity {
// No field is necessary here
}
Now you can use MyEntityWriter for saving all the values and MyEntityReader for loading only the values you need.
I think you will have some problems with schema generation if you try to create the tables because only one of the two will be created:
If MyEntityWriter is the first table created, then no problem
If MyEntityWriter is the second table created, the query will fail because the table already exist and the additional column won't be created.
I haven't tested this solution though, there might be something I haven't thought about.

Understand sql query generated by Hibernate

Let's say i have a class A (mapped with table tbl_a) and a class B (mapped with table tbl_b). These 2 classes (tables) have a relation OneToMany relationship for example.
The class B also have a relation with another class C (table tbl_c). The relation is also OneToMany for example.
I make a query (select query) on table tbl_a via Hibernate Criteria. When i check in the console the sql that hibernate generates, i see all the properties of class A, class B and even class C.
Even if everything is working well, the query is to big and selecting all these properties (columns) may affect the performance.
I don't want all the properties of class B and C. I just want the properties of class A.
Is there a configuration in Hibernate, to not select all the properties of the related tables?
Note: the default Lazy fetchType is used.
Would have been better if we could see what code you have written. However, will try to give heads up
#Entity
#Table(name="a")
public class A{
#Id
#column(name="id")
#GeneratedValue(Strategy=GenerationType.AUTO)
private int id;
// suppose this class is mapped to class B as many to one
#ManyToOne(Fetch=FetchType.EAGER)
#JoinColumn(name="b_id")
private B b;
//Note that it is advisable to keep many to one relationships fetch type as eager. Though it depends on project architecture. Performance wise it fetches only one instance in memory this class is mapped to.
//getter setters
}
#Entity
#Table(name="b")
public class B{
#Id
#column(name="id")
#GeneratedValue(Strategy=GenerationType.AUTO)
private int id;
#OneToMany(fetch=FetchType.LAZY, mappedBy="b",Cascade=CascadeType.ALL)
private Set<A> allA = new HashSet<A>();
//this says that keep a onetomany relationship but do not fetch any of the associated entities until said so. Which is advisable as because If we keep FetchType.EAGER then it will fetch more than one entity for a single entity.
Suppose B entity is related to 10 A entities then it will load all of them as soon as B is fetched in memory, so it will be a performance issue for a semi large application also.
//getter setter
}

JPA - how to model this join with conditions?

I was trying to find an answer but, unfortunately, with no luck.
The data structure looks like this:
TABLE_X - contains userId, also userType telling if this is external or internal user
INTERNAL_USERS - contains key userId
EXTERNAL_USERS - also contains key userId
TABLE_X.userId is either INTERNAL_USERS.userId or EXTERNAL_USERS.userId.
Now, I would like to map an entity out of TABLE_X and have user object mapped to correct entity, either INTERNAL_USERS or EXTERNAL_USERS.
How should I do this?
Should I create two fields and map one to INTERNAL_USERS and one two EXTERNAL_USERS and just see which one is not empty?
If I understand correctly your question, what you have to do is to replicate structure of the TABLE_X columns with fields on the TABLE_X class, and add to fields one for INTERNAL_USERS.userID and one for EXTERNAL_USERS.userID
But if you store on TABLE_X.userType if a user is internal or external, I think that the best thing you can do is not create any other table, because you just have the information you need on your first table (TABLE_X). If you want to know all the users that are internal for instance, just do a SQL and select all the values from TABLE_X where userType = "Internal"
Use Hibernate inheritance. Check out the Table per class inheritance pattern. Here you have both INTERNAL_USERS or EXTERNAL_USERS data in TABLE_X with userType as the discriminator column.
http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/#d0e1191
Given the table structure, you can use JPA Inheritance as detailed here:
https://en.wikibooks.org/wiki/Java_Persistence/Inheritance#Example_joined_inheritance_annotations
In Hibernate you can model such relationships as follows. When querying for a User you can then rely on Hibernate to return an instance of the correct type.
#Entity
#Inheritance(strategy=InheritanceType.JOINED)
public class User{
#Id
private Long userId;
}
#Entity
public class InternalUser extends User{
}
#Entity
public class ExternalUser extends User{
}
As noted in the article linked to, Hibernate does not require a discriminator column to be specified when using joined inheritance. It does however support a discriminator column if one is available. As you have one available in your schema - userType - then you could explicitly specify it if you wish. I imagine this would yield some performance benefit in terms of the generated SQL:
Mappings with optional discriminator column:
#Entity
#Inheritance(strategy=InheritanceType.JOINED)
#DiscriminatorColumn(name="userType")
public class User{
#Id
private Long userId;
}
#Entity
#DiscriminatorValue("INT")
public class InternalUser extends User{
}
#Entity
#DiscriminatorValue("EXT")
public class ExternalUser extends User{
}

Hibernate Inheritance.JOINED generated FK name

I am currently trying to use inheritance within Hibernate and came across InheritanceType.JOINED. I like the idea of concentrating all data in one table and sharing IDs rather than having duplicate columns in all the sub type tables (#MappedSuperClass). But Hibernate automatically generates indexes on my sub class tables on the id column like FK_idx3wiwdm8yp2qkkddi726n8o everytime I initialize my Hibernate singleton. I noticed that by hitting the 64 key limit on my MySQL Table as the names are generated differently on every startup.
What is the proper way to handle this? Can this be fixed by annotations? What else could I try?
I know that there are countless similar Questions on SO but haven't been able to identify one solving my specific problem.
I am not going to disable hbm2ddl.auto during dev mode.
I am using MyISAM. There are no actual Foreign Keys. This is why Hibernate generates default indexes, I think. Anyway, the problem would be identical with InnoDB and real Foreign Keys as the names would still be quite random. Or maybe Hibernate would actually check for existence in this case. I don't really see, why it does not do this on MyISAM tables.
As I hit similar problems before, the solution could also be to specify a name for that single-column index. But how?
Super Class: FolderItem
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class FolderItem implements Comparable<FolderItem>
{
#Id
#GeneratedValue
protected int id;
protected String name;
#OneToOne
#ForeignKey(name = "fkParent")
protected Folder parent;
...
}
Sub Class: Folder
#Entity
public class Folder extends FolderItem
{
#OneToMany(mappedBy = "parent")
#OrderBy(value = "sortOrder")
private List<FolderItem> children;
...
}
What I tried
add #Index to FolderItem.id - this created an index on the FolderItem table as one would expect, but didn't affect the Folder table
copy protected int id; to Folder and tried to add an #Index to it, which resulted in an Exception similar to "duplicate definition of ID"
add #Table(appliesTo = "Folder", indexes = { #Index(name = "fkId", columnNames = { "id" }) }) to Folder class, which actually created my specified index as expected, but still created it's own FK_9xcia6idnwqdi9xx8ytea40h3 which is identical to mine, except for the name
Try #PrimaryKeyJoinColumn(name = "foler_item_id") annotation for Folder class.

JPA Inheritance demanding ID in subclass

I'm working on a project with Struts2 along with JPA with Hibernate.
I have two entities User (id,email) and Customer (user_id).
Since it's natural to think that every Customer is a User I had the Customer Inherit from the user. Here's the relevant code of the entities:
User:
#Entity
#Table(name = "user")
#Inheritance(strategy = InheritanceType.JOINED)
public class User implements Serializable {
#Id
#GeneratedValue
private int id;
private String email;
...getters and setters...
}
Customer
#PrimaryKeyJoinColumn(name = "user_id",referencedColumnName = "id")
#NamedQueries({
#NamedQuery(name = "Customer.getByCustomerUserId", query = "SELECT C FROM Customer C where C.id=:id")
})
public class Customer extends User implements Serializable {
}
If I run the query Customer.getByCustomerUserId I keep getting the error:
Unknown column 'customer0_.id' in 'where clause'
I tried following the answer given here by creating a BaseEntity having the Id field and having the User Entity inherit from it but there's no effect. I still keep getting this error message. Can someone Please tell me what's going wrong?
I suspect that there is no reference in the customer table to the user table, or there is something similar off with the constraints.
I suggest that you rename your entity classes and let Hibernate generate tables from them. Then use your query on the new entities, and if you have no problems, you should inspect the differences between the new tables and the existing ones (use 'Create table script' option in some DB tool).
Drop primaryKeyJoinColumn statement.
Then insert #ForeignKey(name="CHOOSE_A_NAME").
Generate your ddl and run it (with the constraints)
By default, inheritance mecanisms will use the Pk defined in superClass ( whatever his name)
ENjoy

Categories

Resources