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
Related
What my project have:
rsqlParser in order to parse complicated queries
Hibernate envers for audit purposes
Pretty stupid middle developer who don't know how to implement isNull rsql query
I have two Object with strict one-to-one relationship: object A which contains object B, and object B, which contains object A.
In RDS it's looks like object B has an object_a_id field
Object_A entity class
#Entity
#Getter
#Setter
#Audited
#NoArgsConstructor
public class Object_A {
#OneToOne(mappedBy = "object_a")
private Object_B object_b;
}
Object_B entity
#Entity
#Getter
#Setter
#Audited
#NoArgsConstructor
public class Object_B {
#OneToOne
#JoinColumn(
name = "object_a_id",
referencedColumnName = "id",
foreignKey = #ForeignKey(name = "object_b_object_a_fk")
)
private Object_A object_a;
Clearly you see that Object_B OWNS Object_A and when I try to perfom something simple like
return auditProperty.isNull();
I get
This type of relation (object_b) isn't supported and can't be used in queries
I guess I need somehow to make custom query where I add some object_b subselect beforehand but can't figure out how to write it.
You should probably create an issue in the issue tracker(https://hibernate.atlassian.net) with a test case(https://github.com/hibernate/hibernate-test-case-templates/blob/master/orm/hibernate-orm-5/src/test/java/org/hibernate/bugs/JPAUnitTestCase.java) that shows this limitation and ask for an improvement for this.
Usually, in ORM this is handled on the SQL level by introducing an exists subquery like:
where not exists (select 1 from object_b b where b.object_a_id = rootAlias.id)
Not sure how that works exactly in your case, but you could try to do something similar in your query.
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.
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{
}
I want to rewrite the call delete operation (on association table) on a many-to-many association sending by EclipseLink when we use only java code.
Let me explain the goal.
I have 3 tables, person, unit and an associative one : PerInUnit, so a person can be in multiple units and a units can contains many people. But I have some dependances on the PeInUnit table (If the person was present or not on a specific date, another table (Participations)), so I can't (and I don't want) delete a record. For that, I make softs deletes, so I can keep records to make some statistics.
I read already about the Customizer and AdditionalCriteria and I setted them to the PerInUnit class. It works perfectly => when I make an em.remove(myPerInUnit); the sql query sent to the db is Update PER_IN_UNIT SET STATUS='delete' WHERE id = #id; and the specified row as "delete" for status. Also, when I read all records, I don't have them with status "delete". But I use explicitly the PeeInUnit class.
Here is the code :
#Entity
#Table(name = "PER_IN_UNIT")
#AdditionalCriteria("this.status is null")
#Customizer(PIUCustomizer.class)
public class PerInUnit implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "GEN_SEQ_PIU")
#SequenceGenerator(name = "GEN_SEQ_PIU", sequenceName = "SEQ_PIU", initialValue = 1, allocationSize = 1)
#Column(name = "ID")
private Long id;
#ManyToOne(cascade=javax.persistence.CascadeType.PERSIST)
#JoinColumn(name = "PER_ID")
private Person person;
#ManyToOne(cascade=javax.persistence.CascadeType.PERSIST)
#JoinColumn(name = "UNI_ID")
private Unit unit;
#Column(name = "STATUS")
private String status;
//Constructor, getters, setters
}
And the code for the PIUCustomizer :
public class PIUCustomizer implements DescriptorCustomizer {
#Override
public void customize(ClassDescriptor descriptor) {
descriptor.getQueryManager().setDeleteSQLString("UPDATE PER_IN_UNIT SET STATUS = 'delete' WHERE ID = #ID");
}
}
Here come the problem : As I use EclipseLink with bidirectionnal relationship I want to make some instruction like myUnit.getPeople.remove(currentPerson); (remove the current person from the unit "myUnit"). But EclipseLink sent the following instruction (during commit !) :
DELETE FROM PER_IN_UNIT WHERE ((UNI_ID = ?) AND (PER_ID = ?))
instead of the
Update PER_IN_UNIT SET STATUS='delete' WHERE ((UNI_ID = ?) AND (PER_ID = ?))
that I expected and raise (obviously, because of dependances (FKs)) the following exception :
Query: DataModifyQuery(sql="DELETE FROM PER_IN_UNIT WHERE ((UNI_ID = ?) AND (PER_ID = ?))")
at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl.commit(EntityTransactionImpl.java:157)
at test.Crud.update(Crud.java:116)
at test.Test.runTest(Test.java:96)
at test.Test.main(Test.java:106)
Caused by: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLIntegrityConstraintViolationException: ORA-02292: integrity constraint (PEOPLE.FK_PAR_PIU) violated - child record found
Other problem (in the same kind), when I make something like System.out.prinln(myUnit.getPeople()) I have all the people in the unit "myUnit", including them having status 'delete'.
Is it possible to change some code/instructions/Customizer/etc in eclipseLink to change the delete call from person for PerInunit table, or I have to make my own queries and use them instead of using powerful of orm ?
Thanks for your answers and please forgive me for my poor english !
Fab
You should not be getting a delete when you call myUnit.getPeople.remove(currentPerson) unless you mapped Unit to Person with a ManyToMany using the PER_IN_UNIT table. Since you have an entity for the PER_IN_UNIT table, this would be wrong, as it really should be a Unit-> PerInUnit OneToMany mapping and then a PerInUnit -> Person ManyToOne mapping. The myUnit.getPeople.remove(currentPerson) call would then simply be getting the PerInUnit instance and marking its status as deleted, or dereferencing it and letting JPA call remove, thereby using your soft delete SQL query.
By using a ManyToMany mapping for the PER_IN_UNIT table, this mapping is completely independent to your PerInUnit entity mapping, and knows nothing about the entities that maybe cached or the soft deletes required to remove them. If you don't want to map the PER_IN_UNIT table as an entity, see http://www.eclipse.org/forums/index.php/t/243467/ which shows how to configure a ManyToMany mapping for soft deletes.
I want to use hibernate objects in project as defined below.
#Table(name = "Parent")
class Parent{
int id;
String name;
}
#Table(name = "Child")
class Child extends Parent{
String schoolNo;
}
But in the database;
There is no relation with these two table.
Parent tables columns are; id, name
Child tables columns are; id, name and schoolNo
If I use
#Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
when I send a query for Parent object, hibernate use UNION on Child and Parent tables but I want to select from only Parent table.
And if I use
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
hibernate wants a discriminator column.
I need hibernate sends select query for each class to its table.
Best regards.
TABLE_PER_CLASS is the correct strategy here.
It's odd that Hibernate generates a union query over both tables, but that should still work. The subquery over the wrong table won't find anything, so the results will be correct. This sounds like a bug in Hibernate's query generation for subclasses.
In a similar situation, I use #Inheritance(strategy = InheritanceType.JOINED) on the parent table.
See more info in the Hibernate docs: http://docs.jboss.org/hibernate/annotations/3.5/reference/en/html/entity.html#d0e1168