ORM - Use whole object or only identifier as JoinColumn? - java

I have a table/class User and Message. One User can have many messages. How do I model the Java class for Message; should it have a property of type User, or just a String for the username which is the key in this relationship?
When using Hibernate to generate the classes, it gave the Message class a User property. Does that mean it's the right way?
I find it problematic when creating messages to also give it a User instance, because I can't seem to do that in a Spring webflow that I'm using (I'm only passing the username from the view).

you can design your class with a OneToMany relation and Message class does not need to have an user field.
For exemple :
public class User {
#OneToMany
private List<Message> messages;
}
public class Message {
#Basic
private String body;
}
In this case, the database table for Message will have a column for the user id. You can specify another columnn by using JoinColumn :
public class User {
#OneToMany
#JoinColumn(name="my_user_id_column")
private List<Message> messages;
}
Otherwise, you can use #JoinTable like in this answer

Related

How to hardcode group information on field for cascading validation

I have the following bean;
public class Customer {
#NotNull(groups = New.class)
private String id;
#Valid
private List<CustomerDetail> detailList;
}
As you see, I cascade validation down to each CustomerDetail in detailList by annotating the field with #Valid, but I wish to propagate the validation with a hard-coded group, is that possible? Whatever group is supplied for validation, I wish a fixed group, namely New to be active in validation of detailList.
This is due to my conflicting requirements, one wishes to treat details as a sub-resource of Customer therefore I need full validation on it all the time when it is validated within a customer pojo. Another requirement is to treat each detail as a separate resource, therefore I need to do patch for some fields, so when it is validated separately, different groups can be applied.
public class CustomerDetail {
#NotNull(groups = New.class)
private String desc;
private String remark;
}
So when it is any sort of operation for Customer, every CustomerDetail in customerList should use New group, even if Customer does not necessarily use that group for validation.
In a way, I want to do this;
public class Customer {
#NotNull(groups = New.class)
private String id;
#Validated(New.class)
private List<CustomerDetail> detailList;
}
But I was unable to find such a feature, I wanted to do this to evade creating multiple groups, which was deemed confusing.
You need to introduce your own annotation to have class level constraints. Create a custom annotation with own validation logic implemented in the validator.
See the chapter 6.2. Class-level constraints of the doc
Or see the example

JPA Design of User and Roles

I have a User class and a Role class. Both of these classes are JPA entities, and hence stored in a Person table and a Role table, as well as a corresponding link table Person_Role used for joins, since the association is many to many. A user may have many roles, and a role may be assigned to many users.
#Entity
#Table(name="role")
public class Role implements Comparable<Role>
{
// data members
#Id #GeneratedValue
private int id; // primary key
private String name; // name of the role
private String description; // description of the role
...
}
#Entity
#Table(name="person")
public class Person implements Comparable<Person>
{
// data members
#Id #GeneratedValue
protected int id; // the primary key
protected String username; // the user's unique user name
protected String firstName; // the user's first name
protected String lastName; // the user's last name
protected String email; // the user's work e-mail address
#Transient
protected String history; // chronological list of changes to the person
// don't want to load this unless an explicit call to getHistory() is made
#Transient
protected Set<Role> roles; // list of roles assigned to the user
// don't want to load this unless an explicit call to getRoles() is made
...
}
The User entity is used extensively throughout the application, as it is a shared reference for many objects, and is used in many, many searches. 99.99% of the time, the user's roles and history are not needed. I'm new to JPA, and have been reading the "Java Persistence with Hibernate" book in order to learn. As I understand lazy fetching, it will load all the corresponding User data from the database when any getXXX() method is called.
Ex: user.getFirstName() would cause a database hit and load all the data, including roles and history, for the user.
I want to avoid this at all costs. Its just needless in 99.99% of the use cases. So, what's the best way to handle this?
My initial thought is to mark the Set<Role> roles and Set<String> history in the User class as #Transient and manually query for the roles and history only when the user.getRoles() or user.getHistory() method is called.
Thanks for any suggestions.
As I understand lazy fetching, it will load all the corresponding User
data from the database when any getXXX() method is called.
You can force JPA to be eager or lazy while fetching data from the database but first and foremost it depends on JPA provider. As described in JPA 2.1 specification, chapter 11.1.6:
The FetchType enum defines strategies for fetching data from the
database:
public enum FetchType { LAZY, EAGER };
The EAGER strategy is a requirement on the persistence provider
runtime that data must be eagerly fetched. The LAZY strategy is a
hint to the persistence provider runtime that data should be fetched lazily when it is first accessed. The implementation is permitted to
eagerly fetch data for which the LAZY strategy hint has been
specified. In particular, lazy fetching might only be available for
Basic mappings for which property-based access is used.
A nice presentation on how fetching strategies work and how performant they are in real-life scenarios you can find here.
Ex: user.getFirstName() would cause a database hit and load all the
data, including roles and history, for the user.
Data are retrieved either directly from the persistence context (usually it has a short lifespan) or indirectly from the underlying database (when it's not found in the transactional/shared caches). If entity manager is requested to get your entity object and it does not exist in the persistence context it needs to go deeper - into the database in the worst scenario.
I want to avoid this at all costs. Its just needless in 99.99% of the
use cases. So, what's the best way to handle this?
An example approach:
#Entity
#NamedQuery(name="Person.getNameById",
query="SELECT p.name FROM Person p WHERE p.id = :id")
public class Person
{
#Id #GeneratedValue
protected int id;
private String name; //the sole attribute to be requested
#ManyToMany //fetch type is lazy by default
#JoinTable
protected Set<Role> roles; //not loaded until referenced or accessed
...
}
Usually the best way to go is the find method. It's perfect when you want to retrieve all non-relationship attributes at once:
Person p = em.find(Person.class, id)
An alternative for you would be to use named query. It's useful when you need a single attribute or a small subset of attributes:
String name = em.createNamedQuery("Person.getNameById", String.class)
.setParameter("id", id)
.getSingleResult()
My initial thought is to mark the Set roles and Set history in the
User class as #Transient and manually query for the roles and history
only when the user.getRoles() or user.getHistory() method is called.
Transient attributes are not persisted in a database. Whatever you will set to these attributes it will stay in memory only. I would prefer JPA doing it lazily.
It will not Load all the data just the the relative to
Person entity = (Person) this.em.find(Person.class, id);
in lazy fetching it will issue a select statement from only the table person, as for protected Set<Role> roles;it will not be loaded but replaced with a proxy object
Hibernate uses a proxy object to implement lazy loading. When we request to load the Object from the database, and the fetched Object has a reference to another concrete object, Hibernate returns a proxy instead of the concrete associated object.
Hibernate creates a proxy object using bytecode instrumentation (provided by javassist). Hibernate creates a subclass of our entity class at runtime using the code generation library and replaces the actual object with the newly created proxy.

Override Spring data JPA id annotation.

I'm using Spring-data-Jpa where I've an entity
#Entity(name="person")
public class Person implements Serializable {
#javax.persistence.Id
private long dbId;
#Id
private final String id;
// others attributes removed
}
In above class I've two different ids id (marked with org.springframework.data.annotation.Id) and dbId(marked with javax.persistence.Id) , since my id field is always populated with a unique identifier (for Person class which I'm getting from somewhere else) so while using Spring JpaRepository it always tries to update the record and since it's not in db, nothing happens.
I've debug code and saw that it uses SimpleKeyValueRepository which gets the id field which is id, and thus it always gets a value and tries to update record, can I override this behavior to use dbId instead of id field? Is there any way to achieve same with some configuration or annotation, any help is greatly appreciated.
Each entity must have exactly one #Id. On the other hand, you might want to declare a column as unique. It can be done by:
#Entity(name="person")
public class Person implements Serializable {
#Id
private Long id;
#Column(unique = true)
private final String uuid;
// others attributes removed
}
Also remember, that Spring Data JPA id should be reference Long instead of a primitive as you want to save objects with id = null.
String id should probably be String uuid and be initialized as String uuid = UUID.randomUUID().toString();
Similar situation would be an unique email requirement for user. On one hand it'll be a primary key, but on the other, you won't mark it as #Id.
If you need further clarification or your environment is more complicated, just ask in comments section below.

Restrict documents returned by spring-data-rest

A class Dog is using a private field owner to keep the owner username kept in class User (which implements UserDetails):
#Document
public class Dog {
#Id
private ObjectId id;
private String owner;
}
#Document
public class User implements UserDetails {
#Id
private ObjectId id;
private String username;
}
These documents are persisted using spring-data-mongodb and exposed using spring-data-rest. User is authenticated using Spring Security, so it is available as #AuthenticationPrincipal.
It is requested that REST can only access to the dog the user owns. Is it possible to tweak spring-data-rest to return only a subset of the documents from collection dogs, the ones with 'owner' field is the same as the username in the User instance returned by the authentication principal ?
What I did in such a scenario was implementing custom RepositoryInvoker.
The invoker was a proxy for the one created by Spring Data Rest. The idea was to intercept invokeFindAll (or other method that should filter by the ownership) and delegate to the the query method that implements the filtering.
Note the I was using Spring Data JPA and Specfications to implement the filtering predicate. I believe similar approach should by feasible with Mongo.

Store JPA entity with given field object id instead of object itself

The problem is following:
We have entity:
#Entity
public class Feedback {
#Id
#GeneratedValue(generator="token")
private String id;
#ManyToOne
private Product product;
private String message;
// other fields
}
And we have a server endpoint, that receives feedback from clients.
Feedback received in multipart/form-based format, with fields:
ProductId - product identifier
Message - feedback message
Some other fields
To set Feedback.product we need to load Product object from JPA - this can be time-consuming and it creates unnecessary queries.
Is it possible to store entity, but pass the product id instead of the product object?
We need some way to modify the INSERT query.
We use EclipseLink JPA with Spring and Vaadin.
Use EntityManager.getReference(): if the entity is not loaded already, it simply creates a lazy-initialized proxy around the ID, without causing any SQL query to be executed.

Categories