So I encountered this issue with updating an entity in DB. while Passing a whole entity and updating only specific fields it treats untouched fields as null, as a result I get an exception since those fields are #Not-Null,
I have tried looking for similar problems but could not fix my problem.
Company ENTITY:
#Entity
#Table (name = "companies")
#Data
#ToString(exclude = "perfumes")
#AllArgsConstructor
#NoArgsConstructor
#Builder
public class Company {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#NotNull
private String name;
#NotNull
#Email(message = "Wrong input. please enter a VALID email address")
private String email;
#NotNull
#Size(min = 4, max = 14, message = "Password range must be between 4 - 14 digits")
private String password;
#NotNull
#Enumerated(EnumType.STRING)
private Country country;
#Singular
#OneToMany(mappedBy = "company", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<Perfume> perfumes = new ArrayList<>();
}
Most fields are #NotNull for creation, however, I need to update the entity, sometimes only specific fields.
Service:
#Override
public String updateCompany(int id, Company company) throws DoesNotExistException {
if(!companyRepository.existsById(id))
{
throw new DoesNotExistException(id);
}
companyRepository.saveAndFlush(company);
return company.getName() + " has been UPDATED";
}
as you can see an ENTITY has been passed which causes rest of attributes to be automatically null if not modified.
Controller:
#PutMapping("/updateCompany/{id}")
#ResponseStatus(HttpStatus.ACCEPTED)
public String updateCompany(#PathVariable int id, #RequestBody Company company) throws DoesNotExistException {
return admin.updateCompany(id,company);
}
EXCEPTION:
Validation failed for classes [com.golden.scent.beans.Company] during update time for groups [javax.validation.groups.Default, ]
List of constraint violations:[
ConstraintViolationImpl{interpolatedMessage='must not be null', propertyPath=password, rootBeanClass=class com.golden.scent.beans.Company, messageTemplate='{javax.validation.constraints.NotNull.message}'}
]
Thanks.
The controller is binding the values you pass in to a new Company entity. The new entity is not attached to the persistence context, it does not have the state of the pre-existing entity. When you save it JPA thinks you want to null out all the fields you don't have values for.
Instead, you could have the controller bind its arguments to a DTO. Then in the service you look up the existing Customer, using findById, and copy the fields you want updated from the DTO to the entity. Then call saveAndFlush passing in the updated entity.
It looks like there's an improvement over the DTO, you can use aJsonPatch to hold the updates passed in, see https://www.baeldung.com/spring-rest-json-patch. The patch method seems like a better match for what you're doing anyway.
On the server the important thing is to look up the existing entity so that you have an entity that is attached to the persistence context and has all its fields current.
I have an enttity in my domain:
public final class FieldDefinition {
private final FieldDefinitionId id;
private final FieldName name;
//other fields
public FieldDefinition(final FieldDefinitionId id,
final FieldName name) {
checkState(id, name, label, type);
this.id = id;
this.type = type;
}
private void checkState(final FieldDefinitionId id,
final FieldName name) {
Preconditions.checkState(nonNull(id), "id cannot be null");
Preconditions.checkState(nonNull(name), "name cannot be null");
As you can see FieldDefinitionId is required while object creation. This id is generated in database entity:
#Entity
public final class FieldDefinitionJpaEntity implements Serializable {
#Id
#GeneratedValue(strategy = IDENTITY)
private long id;
#Column(length = 50, nullable = false)
private String name;
....
}
There 'id' is the same as FieldDefinitionId . It is mapped like that:
private FieldDefinition asDomainField(final FieldDefinitionJpaEntity entity) {
return new FieldDefinition(
new FieldDefinitionId(entity.getId()),
new FieldName(entity.getName()),
// other fields
}
Now i am receiving FieldDefinitionDto from another service which needs to be saved to database. Here is the class
class FieldDefinitionDto {
private final String name;
//other fields
}
As you can see there is no id inside DTO object. Now when i am trying to map DTO to domain model i cannot do this because FieldDefinitionId is required. What should i do? Should i create seperate model for creation like FieldDefinitionCreation which is basically the same as FieldDefinition but without FieldDefinitionId ?. I would like to stick to id generation on database level (not on domain level with UUID). Anyone had similar problem? Is keeping 2 seperate models which are almost identical is a good idea?
Your ID generation strategy simply isn't aligned with your domain model design.
#GeneratedValue(strategy = IDENTITY) means the identity will be generated by the DB when the entity gets saved. If you choose this strategy then you can't make id a required constructor argument in the FieldDefinition's class.
I'm also a bit puzzled why you wouldn't map FieldDefinition directly with JPA rather than having FieldDefinitionJpaEntity?
Anyway, either you go with an assigned ID upfront (can still be generated in the DB) e.g.
FieldDefinitionId id = fieldDefinitionRepository.nextId();
FieldDefinition fd = new FieldDefinition(id, name);
or you go with a generated ID upon persisting e.g.
FieldDefinition fd = new FieldDefinition(name); //no ID yet
fieldDefinitionRepository.save(fd); //ID assigned
I always prefer to generate IDs upfront because it's much easier to generate domain events, among other things.
I'm working on a RESTFUL web application with spring and hibernate. I want to make a new USER entity that has ManyToOne relationship with ROLE entity:
#Entity
#Table(name = "roles")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public Long id;
#Column(unique = true)
private RoleEnum name;
}
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public Long id;
private String firstName;
private String lastName;
#ManyToOne
private Role role;
}
In frontEnd html and ajax, I set all fields except ids(id of USER and id of ROLE) and send json string to backend. This is sample json:
{
"firstName": "john",
"lastName": "becks",
"role": {
"name": "STUDENT"
}
}
The problem comes here that I want to assign STUDENT role to this new USER that existed in database but I just know its name not id. And I got error from hibernate:
TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing
Can anyone help solve this problem?
Thanks in advance!
There are many parts missing in your example, but to put it simply, hibernate does not have the Role instance from the relationship in its persistence context. In other words, it's telling you to persist the new Role before persisting the User. Hibernate is clever, it tracks objects fetched form the database and can distinguish between a new and existing entity.
Obviously, that is not what you're trying to do, you want to use an existing Role, not create a new one.
The only way to do that is to load the Role from the database, finding it by using the name, and add that retrieved role to the relationship.
Something like this:
public User createUser(final String userFirstName, final String userLastName, final String roleName) {
Role role = roleDao.findByName(roleName);
if(role == null) {
role = new Role(roleName);
roleDao.save(role);
}
final User user = new User(userFirstName, userLastName);
user.addRole(role);
userDao.save(user);
return user;
}
is there a tutorial for a cost-effective entity design?
If I make a small sample and say I want to store users and groups. These groups have a List of users. If any user wants to join this group I have to check is this group existing and the user isn't part of the group.
My question is not how to do this. The question is for a good entity-design or a good objectify-using.
Here is some shortened sample code how I would do this:
User.java
#Entity
#Cache
#Embed
public class User {
#Id Long id;
#Index String name;
String passwordHash;
}
Group.java
#Entity
#Cache
public class Group {
#Id Long id;
#Index Long groupAdministratorUserId;
#Index String name;
List<User> users = new ArrayList<User>();
#Index Boolean isPublic;
}
using
if (!app.authenticate(getRequest(), getResponse()))
{
// Not authenticated
setStatus(Status.CLIENT_ERROR_UNAUTHORIZED);
}
else
{
Group newGroup = ofy().load().type(Group.class).id(Long.parseLong(id)).now(); // is it correct that the embedded data is already loaded?
// following check and insert is only for illustration!
newGroup.getUsers().contains(connectedUser);
newGroup.getUsers().add(connectedUser);
ofy().save().entity(newGroup).now();
}
My "overhead" (authentication)
public class MyVerifier extends LocalVerifier {
private User fetched;
public User getFetched() {
return fetched;
}
#Override
public char[] getLocalSecret(String identifier) {
// this is behind search... and another list()
// User fetched = ofy().load().type(User.class).filter("name", userName).first().now();
fetched = User.searchByExactName(identifier);
if (fetched != null)
{
return fetched.getPasswordHash().toCharArray();
}
return null;
}
}
P.S. I know the page from google: https://code.google.com/p/objectify-appengine/wiki/BestPractices
But that is not what I'm searching for
I would store the list of groups IDs the User entity. No need to use #Embed. What the best solution is really depends on what the most common operations will be in your application. Based on what you said I would recommend the following:
#Entity
#Cache
public class User {
#Id long id;
String name;
String passwordHash;
List<Long> groups;
// constructor left out for brevity.
}
#Entity
#Cache
public class Group {
#Id long id;
long adminId;
String name;
boolean isPublic;
// constructor left out for brevity.
}
User user1 = new User(userName1, passwordHash1);
User user2 = new User(userName2, passwordHash2);
Key<User> user1Key = ofy().save().entity(user1).now(); // Create two users.
Key<User> user2Key = ofy().save().entity(user2).now(); // The don't have any groups yet.
long adminId = user1Key.getId();
Group group = new Group(name, adminId, isPublic)
Key<Group> groupKey = ofy().save().entity(group).now(); // Create a group
user2.addToGroup(groupKey.getId()); // This adds the group ID to the User.groups list.
ofy().save().entity(user2).now(); // Add user2 to group.
To save costs (specifically on small datastore operations during updates) make sure to create as few indexes as possible. Start with few #Indexes and add them as necessary.
In JPA (Hibernate), when we automatically generate the ID field, it is assumed that the user has no knowledge about this key. So, when obtaining the entity, user would query based on some field other than ID. How do we obtain the entity in that case (since em.find() cannot be used).
I understand we can use a query and filter the results later. But, is there a more direct way (because this is a very common problem as I understand).
It is not a "problem" as you stated it.
Hibernate has the built-in find(), but you have to build your own query in order to get a particular object. I recommend using Hibernate's Criteria :
Criteria criteria = session.createCriteria(YourClass.class);
YourObject yourObject = criteria.add(Restrictions.eq("yourField", yourFieldValue))
.uniqueResult();
This will create a criteria on your current class, adding the restriction that the column "yourField" is equal to the value yourFieldValue. uniqueResult() tells it to bring a unique result. If more objects match, you should retrive a list.
List<YourObject> list = criteria.add(Restrictions.eq("yourField", yourFieldValue)).list();
If you have any further questions, please feel free to ask. Hope this helps.
if you have repository for entity Foo and need to select all entries with exact string value boo (also works for other primitive types or entity types). Put this into your repository interface:
List<Foo> findByBoo(String boo);
if you need to order results:
List<Foo> findByBooOrderById(String boo);
See more at reference.
Basically, you should add a specific unique field. I usually use xxxUri fields.
class User {
#Id
// automatically generated
private Long id;
// globally unique id
#Column(name = "SCN", nullable = false, unique = true)
private String scn;
}
And you business method will do like this.
public User findUserByScn(#NotNull final String scn) {
CriteriaBuilder builder = manager.getCriteriaBuilder();
CriteriaQuery<User> criteria = builder.createQuery(User.class);
Root<User> from = criteria.from(User.class);
criteria.select(from);
criteria.where(builder.equal(from.get(User_.scn), scn));
TypedQuery<User> typed = manager.createQuery(criteria);
try {
return typed.getSingleResult();
} catch (final NoResultException nre) {
return null;
}
}
Best practice is using #NaturalId annotation. It can be used as the business key for some cases it is too complicated, so some fields are using as the identifier in the real world.
For example, I have user class with user id as primary key, and email is also unique field. So we can use email as our natural id
#Entity
#Table(name="user")
public class User {
#Id
#Column(name="id")
private int id;
#NaturalId
#Column(name="email")
private String email;
#Column(name="name")
private String name;
}
To get our record, just simply use 'session.byNaturalId()'
Session session = sessionFactory.getCurrentSession();
User user = session.byNaturalId(User.class)
.using("email","huchenhai#qq.com")
.load()
This solution is from Beginning Hibernate book:
Query<User> query = session.createQuery("from User u where u.scn=:scn", User.class);
query.setParameter("scn", scn);
User user = query.uniqueResult();
I solved a similar problem, where I wanted to find a book by its isbnCode not by your id(primary key).
#Entity
public class Book implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
private String isbnCode;
...
In the repository the method was created like #kamalveer singh mentioned. Note that the method name is findBy+fieldName (in my case: findByisbnCode):
#Repository
public interface BookRepository extends JpaRepository<Book, Integer> {
Book findByisbnCode(String isbnCode);
}
Then, implemented the method in the service:
#Service
public class BookService {
#Autowired
private BookRepository repo;
public Book findByIsbnCode(String isbnCode) {
Book obj = repo.findByisbnCode(isbnCode);
return obj;
}
}
Write a custom method like this:
public Object findByYourField(Class entityClass, String yourFieldValue)
{
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Object> criteriaQuery = criteriaBuilder.createQuery(entityClass);
Root<Object> root = criteriaQuery.from(entityClass);
criteriaQuery.select(root);
ParameterExpression<String> params = criteriaBuilder.parameter(String.class);
criteriaQuery.where(criteriaBuilder.equal(root.get("yourField"), params));
TypedQuery<Object> query = entityManager.createQuery(criteriaQuery);
query.setParameter(params, yourFieldValue);
List<Object> queryResult = query.getResultList();
Object returnObject = null;
if (CollectionUtils.isNotEmpty(queryResult)) {
returnObject = queryResult.get(0);
}
return returnObject;
}
Edit: Just realized that #Chinmoy was getting at basically the same thing, but I think I may have done a better job ELI5 :)
If you're using a flavor of Spring Data to help persist / fetch things from whatever kind of Repository you've defined, you can probably have your JPA provider do this for you via some clever tricks with method names in your Repository interface class. Allow me to explain.
(As a disclaimer, I just a few moments ago did/still am figuring this out for myself.)
For example, if I am storing Tokens in my database, I might have an entity class that looks like this:
#Data // << Project Lombok convenience annotation
#Entity
public class Token {
#Id
#Column(name = "TOKEN_ID")
private String tokenId;
#Column(name = "TOKEN")
private String token;
#Column(name = "EXPIRATION")
private String expiration;
#Column(name = "SCOPE")
private String scope;
}
And I probably have a CrudRepository<K,V> interface defined like this, to give me simple CRUD operations on that Repository for free.
#Repository
// CrudRepository<{Entity Type}, {Entity Primary Key Type}>
public interface TokenRepository extends CrudRepository<Token, String> { }
And when I'm looking up one of these tokens, my purpose might be checking the expiration or scope, for example. In either of those cases, I probably don't have the tokenId handy, but rather just the value of a token field itself that I want to look up.
To do that, you can add an additional method to your TokenRepository interface in a clever way to tell your JPA provider that the value you're passing in to the method is not the tokenId, but the value of another field within the Entity class, and it should take that into account when it is generating the actual SQL that it will run against your database.
#Repository
// CrudRepository<{Entity Type}, {Entity Primary Key Type}>
public interface TokenRepository extends CrudRepository<Token, String> {
List<Token> findByToken(String token);
}
I read about this on the Spring Data R2DBC docs page, and it seems to be working so far within a SpringBoot 2.x app storing in an embedded H2 database.
No, you don't need to make criteria query it would be boilerplate code you just do simple thing if you working in Spring-boot:
in your repo declare a method name with findBy[exact field name].
Example-
if your model or document consist a string field myField and you want to find by it then your method name will be:
findBymyField(String myField);
All the answers require you to write some sort of SQL/HQL/whatever. Why? You don't have to - just use CriteriaBuilder:
Person.java:
#Entity
class Person {
#Id #GeneratedValue
private int id;
#Column(name = "name")
private String name;
#Column(name = "age")
private int age;
...
}
Dao.java:
public class Dao {
public static Person getPersonByName(String name) {
SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
Session session = sessionFactory.openSession();
session.beginTransaction();
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<Person> cr = cb.createQuery(Person.class);
Root<Person> root = cr.from(Person.class);
cr.select(root).where(cb.equal(root.get("name"), name)); //here you pass a class field, not a table column (in this example they are called the same)
Query query = session.createQuery(cr);
query.setMaxResults(1);
List<Person> resultList = query.getResultList();
Person result = resultList.get(0);
return result;
}
}
example of use:
public static void main(String[] args) {
Person person = Dao.getPersonByName("John");
System.out.println(person.getAge()); //John's age
}
Have a look at:
JPA query language: The Java Persistence Query Language
JPA Criteria API: Using the Criteria API to Create Queries
I've written a library that helps do precisely this. It allows search by object simply by initializing only the fields you want to filter by: https://github.com/kg6zvp/GenericEntityEJB
Refer - Spring docs for query methods
We can add methods in Spring Jpa by passing diff params in methods like:
List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);
// Enabling static ORDER BY for a query
List<Person> findByLastnameOrderByFirstnameAsc(String lastname);
In my Spring Boot app I resolved a similar type of issue like this:
#Autowired
private EntityManager entityManager;
public User findByEmail(String email) {
User user = null;
Query query = entityManager.createQuery("SELECT u FROM User u WHERE u.email=:email");
query.setParameter("email", email);
try {
user = (User) query.getSingleResult();
} catch (Exception e) {
// Handle exception
}
return user;
}
This is very basic query :
Entity : Student
#Entity
#Data
#NoArgsConstructor
public class Student{
#Id
#GeneratedValue(generator = "uuid2", strategy = GenerationType.IDENTITY)
#GenericGenerator(name = "uuid2", strategy = "uuid2")
private String id;
#Column(nullable = false)
#Version
#JsonIgnore
private Integer version;
private String studentId;
private String studentName;
private OffsetDateTime enrollDate;
}
Repository Interface : StudentRepository
#Repository
public interface StudentRepository extends JpaRepository<Student, String> {
List<Student> findByStudentName(String studentName);
List<Student> findByStudentNameOrderByEnrollDateDesc(String studentName);
#Transactional
#Modifying
void deleteByStudentName(String studentName);
}
Note:
findByColumnName : give results by criteria
List findByStudentName(String studentName)
Internally convert into query : select * from Student where name='studentName'
#Transactional
#Modifying
Is useful when you want to remove persisted data from database.
Using CrudRepository and JPA query works for me:
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
public interface TokenCrudRepository extends CrudRepository<Token, Integer> {
/**
* Finds a token by using the user as a search criteria.
* #param user
* #return A token element matching with the given user.
*/
#Query("SELECT t FROM Token t WHERE LOWER(t.user) = LOWER(:user)")
public Token find(#Param("user") String user);
}
and you invoke the find custom method like this:
public void destroyCurrentToken(String user){
AbstractApplicationContext context = getContext();
repository = context.getBean(TokenCrudRepository.class);
Token token = ((TokenCrudRepository) repository).find(user);
int idToken = token.getId();
repository.delete(idToken);
context.close();
}