Use of Java constructors in persistent entities - java

I'm new to JPA and using persistence in Java anyway and I have two questions I can't quite work out: I have generated tags as:
#JoinColumn(name = "UserName", referencedColumnName = "UserName")
#ManyToOne(optional = false)
private User userName;
#JoinColumn(name = "DatasetNo", referencedColumnName = "DatasetNo")
#ManyToOne(optional = false)
private Dataset datasetNo;
But in one of the constructors for the class, no reference is made to columns UserName or DatasetNo whereas all other columns in the class are referenced in the constructor.
Can anyone tell me why this is? Both columns UserName and DatasetNo are 'foreign keys' on the entity Visualisation which corresponds to a database table of the same name. I can't quite work out the ORM.
And when using entity classes, or POJO, is it better to have class variables like:
private User userName;
Where an instance of a class is specified or simply the key of that class instance like:
private String userName;
Thanks
Mr Morgan.

The way the constructor was written is just a feature of whichever auto-generation tool you used to create your classes from the schema. If you want a constructor that takes them, feel free to add it. Would need to know which one you used and how it was configured to comment on why it worked that way :)
It's normally preferrable to have the actual object relationships mapped. That is the entire point of ORM afterall, no? To have an actual, usable domain of objects that maps to what's in the database, rather than dumb structs that require further manipulation to be made into usable business objects. You'll also need them if you want to be able to write object queries sensibly.
It's much nicer to write:
Select roles from UserRoles roles where role.user.isAdmin = true
than
Select roles from UserRules roles join Users u on roles.userName = u.userName where u.isAdmin = true
Depending on your display technology it can also be helpful for view binding to have real object relationships.
Do note the property names created by your autogeneration tool are also arbitrary. There is no requirement they match the column names.
public User getUserName() is in fact rather silly.
You can certainly change it to public User getUser()

First of all, per specification, an entity must have a public or protected no-arg constructor. It may have other constructors but JPA doesn't care of them so feel free to add whatever you want/need as long as you provide a no-arg constructor.
Secondly, when working with an ORM, you need to "forget columns and foreign keys" and think objects and associations, the ORM will take care of foreign keys and joins for you.
So I would actually expect to see something like this:
#JoinColumn(name = "UserName", referencedColumnName = "UserName")
#ManyToOne(optional = false)
private User user;
#JoinColumn(name = "DatasetNo", referencedColumnName = "DatasetNo")
#ManyToOne(optional = false)
private Dataset dataset;
The difference is subtile (erf, there is technically no difference) but fundamental in what it communicates: attributes of your entities are not foreign keys but instances of other entities (hence the think object and associations).
The same applies when writing queries using JPQL. You need to think object and to navigate through associations. For example, to retrieve in JPQL the userName of a User for a given... well, let's call Foo the entity holding User and Dataset here:
select f.user.userName from Foo f where f.id = :id

Related

Assigning a column value instead of an Entity in a JPA #OneToOne mapping

In my database, I have tables for users, roles, and classifications. A user can have exactly one role and one classification. The tables do not contain any foreign keys. Instead, I have two join tables; user_roles and user_classifications. Each row in these tables relates a user_id to a role_id and a classification_id. This is preferable because the same role/classification can be assigned to multiple unique users. This obviates the need to have multiple rows with the same role/classification but with different foreign keys pointing to the user.
The roles and classifications tables are relatively simple, an id column and name column, the name being a VARCHAR type. I have a Spring app where the UserRepository extends JpaRepository, and the User model contains javax.persistence annotations. Here it is:
#Entity
#Table(name = "users")
public class User {
#Id
private String id;
// ... other fields here
#OneToOne
#JoinTable(
name = "user_classifications",
joinColumns = #JoinColumn(
name = "user_id",
referencedColumnName = "id"
),
inverseJoinColumns = #JoinColumn(
name = "classification_id",
referencedColumnName = "id"
))
private Classification classification;
#OneToOne
#JoinTable(
name = "user_roles",
joinColumns = #JoinColumn(
name = "user_id",
referencedColumnName = "id"
),
inverseJoinColumns = #JoinColumn(
name = "role_id",
referencedColumnName = "id"
))
private Role role;
// ... the rest of the model
}
#Entity
#Table(name = "roles")
public class Role {
#Id
private String id;
#Column(name = "role_name")
private String name;
}
#Entity
#Table(name = "classifications")
public class Classification {
#Id
private String id;
#Column(name = "classification_name")
private String name;
}
Initially, the role and classification fields were String types. Jpa did not like that though as it was expecting to map to an object that mirrored the respective table. So to make Jpa happy, I created additional POJOs for Role and Classification. And that works, I can see the objects being returned from Jpa, including the id and name fields. The problem is that my front end is expecting String types. I could modify the front end to "inspect" the returned models and only keep the relevant String data (the name). I have to imagine that I am missing something here and simply setting the String value from the object is possible. I feel like I am asking the wrong questions when searching for an answer on Google. I'm hoping by explaining my situation here I'll get better results. Your help is highly appreciated.
This does not really answer your question but it would also solve your problem.
Using entities in your, I assume, REST API is definitely not suggested. You should keep your core model defined as it makes more sense according to your business case. You should then map this core model based on the entities to DTOs that organize the model to the way it suits best the needs of someone consuming your API.
Having said that, you should try to decouple your entities from the model you make available in your API. This would allow you to change your core model and keep your API model untouched. Or the other way around. Hence, I strongly suggest you creating an API model that suits your consumers needs and mapping your entities to this model.
As suggested by João Dias, you should be using DTOs and I think this is a perfect use case for Blaze-Persistence Entity Views.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.
A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:
#EntityView(User.class)
public interface UserDto {
#IdMapping
String getId();
#Mapping("role.name")
String getRoleName();
#Mapping("classification.name")
String getClassificationName();
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
UserDto a = entityViewManager.find(entityManager, UserDto.class, id);
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
Page<UserDto> findAll(Pageable pageable);
The best part is, it will only fetch the state that is actually necessary!

Hibernate JPA uniqueness

Let's say that this is a class that has unique constrained field.
#Entity
public class Thing {
#Column(name = "name", unique = true)
private String name;
#ManyToOne
private Owner owner;
}
Example works just fine if new Things are created with unique names. But when different owners want to create things with the same name this approach fails.
Is it possible to set unique constraint to differ records of Things in the database based on the Owners using Hibernate/JPA functionalities (I could not find any) or should I write my own logic and dump the unique from #Column.
Perhaps it could be done with Hibernate Validator? Reading the docs I haven't found much about unique constraints.
You're looking for #UniqueConstraint
http://docs.oracle.com/javaee/5/api/javax/persistence/UniqueConstraint.html

JPA/Hibernate: Map many-to-many relationship when join table has own primary key

I have three tables with simple structure:
pub [id, name]
days [id, name]
pub_days [id, pub_id, days_id]
For some unholy reason, somebody thought that compound identity for pub_days table (that would be pub_id + days_id) is not enough and added own primary key. I can't change it now, other and larger system depends on that. #sigh
I am trying to map this to Hibernate with standard #ManyToMany JPA annotation like so (I omitted getters, setters, #Entitiy annotations and other clutter):
class Pub {
#ManyToMany(cascade = {CascadeType.ALL})
#JoinTable(name = "pub_days",
joinColumns = {#JoinColumn(name = "pub_id")},
inverseJoinColumns = {#JoinColumn(name = "days_id")})
#OrderBy("id")
private List<Day> pubOpeningDays;
}
class Day {
#Id Long id;
String name.
}
when I execute following code:
Day day = repository.find(Day.class, id);
pub.getPubOpeningDays().add(day);
repository.persist(pub);
I get this error:
ERROR: ORA-01400: cannot insert NULL into ("PUB"."pub_days"."id")
Sadly, that makes perfect sense, because I haven't mapped that ID anywhere. The thing is, I don't even want to. I want it to be generated, but not sure how do I overcome this issue with #ManyToMany mapping. Any ideas?
What you can do is like I mentioned in my comments you can create a separate entity CD which will in turn connect with two classes A and B, Now relationship would be many to many between A and B, and hence A (many to many) CD (many to many) B. Now as per your requirement whenever you need to fetch the instance of A or B, what you can do is simply fire a query in the DB with proper parameters i.e id of a or id of b this will help you get your required result.
I only see two choices, either you change your mapping to a list of PubDay as samwise-gamgee told you in the comments or you add a trigger on insert on table pub_days which set a value for the column id if it is null (it could be using a sequence). But this approach depends on the features supported by your DB.

Hibernate many to many - fetch method eager vs lazy

New to Hibernate.
I have User Group many to many relation.
Three tables : User , Group and UserGroup mapping table.
Entities:
#Entity
#Table(name = "user")
public class User {
#Id
#Column (name = "username")
private String userName;
#Column (name = "password", nullable = false)
private String password;
#ManyToMany(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
#JoinTable(name="usergroup",
joinColumns={#JoinColumn(name="username")},
inverseJoinColumns={#JoinColumn(name="groupname")})
private Set<Group> userGroups = new HashSet<Group>();
... setter and getters
#Entity
#Table(name = "group")
public class Group {
#Id
#Column(name = "groupname")
private String groupName;
#Column(name = "admin", nullable = false)
private String admin;
#ManyToMany(mappedBy = "userGroups", fetch = FetchType.EAGER)
private Set<User> users = new HashSet<User>();
... setter and getters
Notice that in Group Entity I'm using fetch method EAGER.
Now, when I'm calling my DAO to retrive all the groups in the system
using the following criteria:
Criteria criteria = session.createCriteria(Group.class);
return criteria.list();
I'm getting all the rows from the mappgin table (usergroup) instead of getting the actual number of groups...
for example if i have
in user table
username password
-----------------
user1 user1
user2 user2
in group table
groupname admin
---------------
grp1 user1
grp2 user2
in usergroup table
username groupname
------------------
user1 grp1
user2 grp2
user1 grp2
user2 grp1
The result will be the following list - {grp1,grp2,grp2,grp1}
Instead of {grp1,grp2}
If I change in Group Entity the fetch method to LAZY I'm getting the correct results
but hibernate throws me LazyException in another place...
Please assist what fetch method should I use and why ?
Thanks!
Lazy people will tell you to always use FetchType.EAGER counter-intuitively. These are the people who generally don't worry about database performance and only care about making their development lives easier. I'm going to say you should be using FetchType.LAZY for the increased performance benefit. Because database access is usually the performance bottleneck of most applications, every little bit helps.
If you do actually need to get a list of users for a group, as long as your call getUsers() from within a transactional session, you won't get that LazyLoadingException that is the bane of all new Hibernate users.
The following code will get you all groups without populating the list of users for those groups
//Service Class
#Transactional
public List<Group> findAll(){
return groupDao.findAll();
}
The following code will get you all groups with the users for those groups at the DAO level:
//DAO class
#SuppressWarnings("unchecked")
public List<Group> findAllWithUsers(){
Criteria criteria = getCurrentSession().createCriteria(Group.class);
criteria.setFetchMode("users", FetchMode.SUBSELECT);
//Other restrictions here as required.
return criteria.list();
}
Edit 1: Thanks to Adrian Shum for this code
For more info on the different types of FetchMode's see here
If you don't want to have to write a different DAO method just to access your collection object, as long as you are in the same Session that was used to fetch the parent object you can use the Hibernate.initialize() method to force the initialisation of your child collection object. I would seriously not recommend that you do this for a List<T> of parent objects. That would put quite a heavy load on the database.
//Service Class
#Transactional
public Group findWithUsers(UUID groupId){
Group group = groupDao.find(groupId);
//Forces the initialization of the collection object returned by getUsers()
Hibernate.initialize(group.getUsers());
return group;
}
I've not come across a situation where I've had to use the above code, but it should be relatively efficient. For more information about Hibernate.initialize() see here
I have done this in the service layer rather than fetching them in the DAO, because then you only have to create one new method in the service rather than making a separate DAO method as well. The important thing is that you have wrapped the getUsers() call within the transaction, so a session will have been created that Hibernate can use to run the additional queries. This could also be done in the DAO by writing join criteria to your collection, but I've never had to do that myself.
That said, if you find that you are calling the second method far more than you are calling the first, consider changing your fetch type to EAGER and letting the database do the work for you.
Although answer from JamesENL is almost correct, it is lacking of some very key aspect.
What he is doing is to force the lazy-loading proxy to load when the transaction is still active. Although it solved the LazyInitialization error, the lazy loadings are still going to be done one by one, which is going to give extremely poor performance. Essentially, it is simply achieving the same result of FetchType.EAGER manually (and with a even worse way, because we missed the possibilities of using JOIN and SUBSELECT strategy), which even contradict with the concern of performance.
To avoid confusion: Using LAZY fetch type is correct.
However, in order to avoid Lazy Loading Exception, in most case, you should have your repository (or DAO?) fetch the required properties.
The most inefficient way is to do it by accessing the corresponding property and trigger the lazy loading. There are some really big drawbacks:
Imagine what happen if you need to retrieve multiple level of data.
If the result set is going to be big, then you are issuing n+1 SQLs to DB.
The more proper way is to try to fetch all related data in one query (or a few).
Just give an example using Spring-data like syntax (should be intuitive enough to port to handcraft Hibernate Repository/DAO):
interface GroupRepository {
#Query("from Group")
List<Group> findAll();
#Query("from Group g left join fetch g.users")
List<Group> findAllWithUsers();
}
Join fetching is equally simple in Criteria API (though seems only left join is available), quoted from Hibernate doc:
List cats = session.createCriteria(Cat.class)
.add( Restrictions.like("name", "Fritz%") )
.setFetchMode("mate", FetchMode.EAGER)
.setFetchMode("kittens", FetchMode.EAGER)
.list();

Hibernate List is empty when using lazy loading but it has results when eagerly loaded

I two entities a User and a Place witch are bound with many to many association.
When I try to get all the places for a given user thought the getter method, an emtpy list is returned but the user is bound to the place in the database and if I change the default fetching strategy to eager I can see all the places just fine.
I am using MySQL for the persistance.
The annotations used are:
for the User entity:
#ManyToMany
#JoinTable(name= "USER_PLACE",
joinColumns = {#JoinColumn(name="USER_ID")},
inverseJoinColumns = {#JoinColumn(name="PLACE_ID")})
private List<Place> places = new ArrayList<Place>();
and for the Place entity:
#ManyToMany(mappedBy = "places")
private List<User> users = new ArrayList<User>(0);
What can it be the cause of this?
To me, it looks like a Mapping issue in Your domain-model. Do the provided entities also map somewhere else? You might run into crazy joins with other tables. Could you provide all relevant entities? Also, the SQL statements generated by Hibernate for User.getPlaces() would be helpful.

Categories

Resources