JPA/Hibernate - shared primary key - java

I wanted to create bidirectional one to one relationship with shared primary key.
As it is stated here JPA Hibernate One-to-One relationship I have:
#Entity
public class UserProfileInformation {
#Id
#GeneratedValue(generator = "customForeignGenerator")
#org.hibernate.annotations.GenericGenerator(
name = "customForeignGenerator",
strategy = "foreign",
parameters = #Parameter(name = "property", value = "userEntity")
)
long id;
private long itemsPerPage;
#OneToOne(mappedBy="userProfileInformation")
private UserEntity userEntity;
...}
#Entity
#Table(name = "UserTable")
public class UserEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String publicName;
private String password;
private String emailAddress;
private String name;
private boolean active;
#OneToOne(cascade=CascadeType.ALL)
#PrimaryKeyJoinColumn
private UserProfileInformation userProfileInformation;
...}
now, when I try to persist my user in the database, I am getting org.hibernate.id.IdentifierGenerationException: null id generated for:class pl.meble.taboret.model.UserProfileInformation. Is it because, when userProfileInformation is persisted to the database userEntity doesn't have id generated at that point?
Also, what can I do to create bidirectional relationship with shared primary key in my example?
EDIT:
Requested code, this is simple controller to test the operation of persisting UserEntity.
#Controller
#RequestMapping("/test")
public class TestController {
#Autowired
UserDao userDao;
#RequestMapping(method= RequestMethod.GET)
public String t(Model model){
UserEntity entity=new UserEntity();
entity.setActive(false);
entity.setEmailAddress("a");
entity.setName("name");
entity.setPassword("qqq");
entity.setPublicName("p");
UserProfileInformation p = new UserProfileInformation(entity);
entity.setUserProfileInformation(p);
userDao.addUser(entity);
return "login";
}
}

I think the problem is with the id generation strategy. For hibernate #GeneratedValue(strategy = GenerationType.AUTO) translates into a native identifier generation. This means that hibernate expects an identity id field for the UserTable.
I don't know exactly how SQLite works in terms of identity columns, but it seems from this SO question is a little different (see the second answer).
Anyway if you plan to run your application on multiple databases is better for portability to change the id generation strategy from GenerationType.AUTO and use hibernate enhanced generators: SequenceStyleGenerator or TableGenerator. See this link in the hibernate documentation.
EDIT:
I tried to reproduce your problem, and it seems that SQLite dialect is not among the officially supported hibernate dialects. Meanwhile I tested your case with the H2 embeded database and it works as expected: your mappings are correct.
If you are using an unofficial SQLite dialect it might be a bug with this dialect.

Related

Adding both object and foreign key to a Spring Data JPA entity

Below is an example of how I am defining my JPA entity. I am a bit confused on how to have both the object and the foreign key specified for ApplicationUser. I initially just had the ApplicationUser user property, but then realized I want to be able to query on the applicationUserId by defining a method in the ProductRepository such as findByApplicationUserId(long applicationUserId) and the repo keeps saying field does not exist. This is why I tried adding an applicationUserId property to the class and now there appears to be interference when running the application.
#Entity
public class Product{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
private long applicationUserId;
#ManyToOne
#JoinColumn(name = "application_user_id")
private ApplicationUser user;
}

How to stop Hibernate from eagerly fetching a relationship when it is mapped using a column (referencedColumnName) different than the primary key?

I'm mapping a relationship that does not use the entity's primary key. Using "referencedColumnName" with a column different than the primary key causes hibernate to eagerly fetch the association, by issuing an extra select, even when it's tagged with FetchType.LAZY.
My goal is to make it behave like a regular mapping, meaning it wouldn't issue an extra query every time I need to query the main entity.
I have already tried using #LazyToOne(LazyToOneOption.NO_PROXY), which sorts out the problem, but it does not operate well with Jackson's (JSON parsing library) module "jackson-datatype-hibernate5", which skips hibernate lazy proxies when serializing the results.
Here is a scenario almost like the one I have that causes the problem:
Entities:
#Entity(name = "Book")
#Table(name = "book")
public class Book
implements Serializable {
#Id
#GeneratedValue
private Long id;
private String title;
private String author;
#NaturalId
private String isbn;
//Getters and setters omitted for brevity
}
#Entity(name = "Publication")
#Table(name = "publication")
public class Publication {
#Id
#GeneratedValue
private Long id;
private String publisher;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(
name = "isbn",
referencedColumnName = "isbn"
)
private Book book;
#Column(
name = "price_in_cents",
nullable = false
)
private Integer priceCents;
private String currency;
//Getters and setters omitted for brevity
}
Repository (Spring-Data, but you could try directly with the EntityManager):
#Repository
public interface PublicationRepository extends JpaReadRepository <Publication, Long>
{
#Query ("SELECT d FROM Publication d WHERE d.publisher = ?1 ")
Optional <Publication> findByPublisher (String isbn);
}
Thanks
The only way to achieve what you are looking for is by moving the annotatation #Id to the isbn property.
You can leave the #GeneratedValue on the autoincrement property.
Notes:
1 - Make sure that your equals/hc are following the OID(Object ID) on your domain case the "NaturalId" ISBN.
2 - It will be good to ensure if possible on DB level that your natural ID has unique contraint on it.

Get generated entity Id before persisting, Hibernate, Spring data

Some preconditions:
I am not using Oracle DB sequence generator. Instead of it, I rely on the Hibernate sequence generator e.x.
#Entity
#Table(name = "JPA_ENTITY_A")
#GenericGenerator(name = "system-uuid", strategy = "uuid2")
public class JpaEntityA{
#Id
#Type(type = "uuid-binary")
#GeneratedValue(generator = "system-uuid")
private UUID id;
#Column(name="NAME_WITH_ID")
String nameWithGeneratedId;
}
What I want is to persist the following generated value into the column "NAME_WITH_ID": this.nameWithGeneratedId+this.id
Is it feasible to do the following:
public String getNameWithGeneratedId(){
return this.nameWithGeneratedId+this.id;//hope that the returned value will be persisted
}
Or is it possible to retrieve in advance before persisting entity to the DB generated id? If yes, then how can I accomplish it? (based on the comments below it is not possible to do it)
Thx in advance.
You can't; the act of persisting itself is what creates the ID.
In your case, which uses a UUID generator, I think you can use the Lifecycle interface and implement the onSave method returning NO_VETO.
e.g.
#Entity
public MyEntity implements Lifecycle {
#Id
#GeneratedValue
private UUID id;
...
public boolean onSave(final Session session) throws CallbackException {
// here your entity will have an ID
return Lifecycle.NO_VETO;
}
...
}
The onSave method will be called before saving the entity and after generating the ID.

JPA repository - improve findAll() performances

I am using JPA repository, I need to retrieve a whole Mysql table(40000 records) wich has 5 foreign keys towards smaller tables (500 records). I need one field of each of these 5 tables.
If I call a JPArepository findall(), it takes a few seconds to retrieve all the data.
I need it to be faster. Is there a way to do that?
I don't know what would be the best solution, if it can be done on mysql side, or must be done on Java side.
All the tables are well mapped to JPA entities :
#Entity
#Table(name = "T_CLIENT")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Client implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "code")
private String code;
#OneToOne
private Seller seller;
#OneToOne
private Language language;
#OneToOne
private Address address;
#OneToOne
private Country billCountry;
#OneToOne
private ClientType clientType;
}
Thank you for your answers.
You can choose loading fields from foreign keys tables using EntityGraph mechanism.

Hibernate and JPA: how to make a foreign key constraint on a String

I am using Hibernate and JPA. If I have two simple entities:
#Entity
#Table(name = "container")
public class Container {
#Id
#Column(name="guid")
private String guid;
}
#Entity
#Table(name="item")
public class Item {
#Id
#Column(name="guid")
private String guid;
#Column(name="container_guid")
private String containerGuid;
}
and I want to insure that inserting an Item fails if the referenced Container does not exist. I would prefer not to have a Container object populated inside the item object (ManyToOne), how would I do this if it is possible to do?
You can declare arbitrary constraint using columnDefinition attribute:
#Column(name="container_guid",
columnDefinition = "VARCHAR(255) REFERENCES container(guid)")
private String containerGuid;
Note, however, that Hibernate doesn't know anything about this constraint, so that, for example, it may not perform inserts in proper order with respect of it and so on.
Therefore it would be better to create a #ManyToOne relationship. If you are afraid of extra SQL query for Container needed to set this property, you can use Session.load()/EntityManager.getReference() to get a proxy without issuing actulal query.
Try using below relationship mapping
RelationShip Mapping
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#ManyToOne()
#ManyToMany()
<>
#JoinColumn(name="<>")

Categories

Resources