Typesafe Primary Key in Hibernate/JPA - java

I am looking for a way to have a typesafe primary key for my entities using generics in Hibernate. Instead of doing this
#Entity
public class User{
#PrimaryKey
Long id
}
I was thinking of doing this...
#Entity
public class User{
#PrimaryKey
PrimaryKey<User,Long> id
}
Or take the type inference even further...
Any ideas? Has anybody ever tried this before? Would you do this by making your class PrimaryKey embeddable?
#Entity
public class User extends MyEntity<Long>//Primary key type{
#PrimaryKey
PrimaryKey<User> id;
}

While it is possible to use a PK class and to use it as a member of entities with the #EmbeddedId, this would typically make all your JQL queries and your Java code more verbose:
select a.addressKey.id from Address a
or
AddressKey addressKey = new AddressKey();
addressKey.setCountry("USA");
addressKey.setId(634);
Address a = entityManager.find(Address.class, addressKey);
So I would personally use this for a real composite key (i.e. not with a single attribute) only.
Actually, I'm really wondering what problem you are trying to solve because at the end, you will have to deal with a Long anyway. I don't really see the added value of a single attribute typesafe primary key.

If you are looking for a typesafe way to identify an entity, what about using its LazyLoadingProxy?

Related

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{
}

JPA / Spring / Delete Entity, type Mismatch (int/long for id)

I have an Entity which uses
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
and i have a JPA Repository for this Entity. Now i want to delete one of those, but the standard method for that is delete(int i), which can't work because my ID's are not Integers, but Longs. So apart from using int for my IDs, what to do here? Can i specify a custom delete method which uses long, like it works with findbyXX(XX) ?
EDIT:
First of all: Yes i am using Data JPA!
I want to do this:
jparepository.delete(id);
In case id is an Integer:
org.hibernate.TypeMismatchException: Provided id of the wrong type for class com.Entity. Expected: class java.lang.Long, got class java.lang.Integer
In case id is a long:
no method found for delete(long)
So i can either change my ID to int, which i don't want to do, or find a way to get the Repository to work with long. And the question is how
Ok turns out it was just a stupid mistake. So my JPARepository looked like this:
public interface EntityRepository extends JpaRepository<Entity, Integer> {
But Integer represents the type of the Entities ID-Field, which is Long in my case.
So i needed to change to ..JpaRepository<Entity, Long>
If you are using Spring Data JPA, the default delete method is:
void delete(T entity);
Look here:
Spring Data JPA Docs
Also, you it's better to use Long than primitive long, because then you can use more methods when validating:
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
Assuming you are talking about spring data JPA, yes you can specify a custom method if you want to delete by something user-defined, but that isn't necessary for a delete by ID. See the spring docs:
http://docs.spring.io/spring-data/data-commons/docs/current/api/org/springframework/data/repository/CrudRepository.html#delete-ID-
You can delete by a generic ID type that corresponds to the type of your entities' ID (in your case Long). This should work.

Ormlite Multiple PK on class I do not have control

I have a class that I cannot change which has multiple #Id (annotated in JPA annotation) fields and some other annotations that ormlite does not support. So I am creating the table using java code to annotate the class.
However, without the ability to add an extra field to the class, the UniqueCombo trick mentioned in Multiple primary keys - ORMlite does not work for me because for example:
class A {
#Id
int key1;
#Id
int key2;
}
with the following Java code to create ormlite table config:
DatabaseField f = new DatabaseField("key1");
f.setId(true);
f.setUniqueCombo(true);
fieldConfig.add(f);
f = new DatabaseField("key2");
f.setUniqueCombo(true);
fieldConfig.add(f);
if I set any of them as PK (setId(true)) then when the data are actually different, the DB will not be happy when I try to create another row with the same key. Are there any way to solve this problem without changing class A?
No, ORMlite allows only one field as a primary key.

Custom wrapper for id key

I want to wrap id in custom class. Like this
#Entity
#Table(name = "USERS")
public class User {
#EmbeddedId
UserId id;
}
#Embeddable
public class UserId implements Serializable {
private Long value;
}
The issue in auto generation value for UserId. What I should do to make #GeneratedValue on value be workable?
BTW, It would be great if id would be initialized automatically itself.
As far as I know Hibernate only generates values for a field marked as the #Id. I found this post and Hardy's answer supports this.
We have tried to do similar and managed it via a pre-insert listener. It was fairly complex and non-ideal though. Also you might find different behaviour on different database palatforms. Using Oracle sequences would mean that you need to assign the value pre-insert (Hibernate does a select to get the value and then an insert) but with MySQL the auto incrementing field would assign the value and hibernate does an insert to generate the auto generated value and then select to find out what the value was.

Is it possible to map a field in an Entity without defining any association?

I've got the following schema in DB (simplified)
MainTable(
ID primary key
SOMEFIELD
CODE_FK1 -- references OtherTable1 CODE (without declared foreign key)
CODE_FK2 -- references OtherTable2 CODE (without declared foreign key)
... Other fields used
)
OtherTable1(
CODE primary key
LABEL
... other fields not used
)
OtherTable2(
CODE primary key
LABEL
... other fields not used
)
I'm asking if there is any way to define my Entity for main table in order to use directly labels from my other tables, i.e without defining entities for these other table.
I cannot change the DB schema, which is really awful (there are labels/code couples everywhere, defined in multiples tables).
And If it was possible, this solution would allow to keep my code simple, since I don't really need these other entities.
I guess it would result something like that:
#Entity
public class MainEntity{
#Id
private Integer ID;
#Column(name="SOMEFIELD")
private String SomeField;
#SomeAnnotation to Join CODE_FK_1 with OtherTable1.CODE
#SomeAnnotation like #Column(name="LABEL", table="OtherTable1")
private String Label1;
#SomeAnnotation to Join CODE_FK_1 with OtherTable1.CODE
#SomeAnnotation like #Column(name="LABEL", table="OtherTable1")
private String Label1;
}
Thanks by advance for your help!
Another possibility would be using the #Formula annotation to fetch the value from the other table. This will automatically generate a subselect whenever you load your Entity.
I think you'll need something like this:
#Entity
public class MainEntity{
#Id
private Integer ID;
#Column(name="SOMEFIELD")
private String SomeField;
#Formula("(SELECT ot1.LABEL FROM OtherTable1 ot1 WHERE ot1.CODE = CODE_FK_1)")
private String Label1;
}
There is little information about this in the [Hibernate docs][1], so you may need some trial and error to get it right (but you should be able to work it out with hibernate.show_sql=true.
There are 2 possible downsides to this approach:
This is hibernate-specific code
This is plain SQL, and may thus be database-specific
HTH
[1]: http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/#entity-hibspec-property hibernate docs
You can use the #SecondaryTable annotation. See this example:
https://github.com/hibernate/hibernate-orm/blob/823a5c1ede1869fd97471e3b8ebe7ec4ac8068e4/hibernate-core/src/test/java/org/hibernate/test/annotations/join/Dog.java#L20-L24

Categories

Resources