Reusing specific fields in Hibernate on a new Entity - java

I need to reuse some specific fields in a table which is already mapped to a Hibernate entity and create a new Entity.
We've got a Table like this:
TABLE `OLDTABLE` (
/// MANY FIELDS
`aPhoneNumber` varchar(30) NOT NULL,
/// MORE FIELDS
`aActive` int(11) NOT NULL,
`aLastPaid` datetime NOT NULL,
/// EVEN MORE FIELDS
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Updates to this are currently managed with Hibernate and there is no need to change that code.
However, for reporting purposes, we need a new query which only returns some of the fields and we would like to get another Entity mapped to just these fields. Something like this:
public class NewEntity{
private String phoneNumber;
private int active;
private Date lastPaid
/// getters & setters ...
}
We want it for ease of use, instead of adding Scalars to the current queries. No updates would be necessary, just queries.
P.S. I am fairly new to Hibernate, but I got most of the basics.
UPDATE: Thank you very much for the answers. We had an emergency at work and I haven't been able to try them. I will do it this coming week as I've working non-stop on some pressing issues.

You can create a DTO instead of an entity, and use a hibernate constructor expression instead, a pseudo example
public class NewDTO{
private String phoneNumber;
private int active;
private Date lastPaid
public NewDTO(final String phoneNumber, int active, Date lastPaid) {
this.phoneNumber = phoneNumber;
this.active= active;
this.lastPaid= lastPaid;
}
/// getters & setters ...
}
and your query
select new your.package.NewDTO(e.phoneNumber, e.active, e.lastPaid) from Entity e

There is nothing wrong in creating a new entity and mapping it to already mapped database table, especially if it is a read-only entity.

Related

No error when violating constraints after inserting data in spring-boot application with spring data jpa

I'm currently learning Spring-Boot and Spring-Data-JPA.
I'm using a postgresql database for storing the data.
My goal is to store ingredients with a unique and custom ID (you just type it in when creating it), but when another ingredient with the same ID gets inserted, there should be some kind of error. In my understanding, this is what happens when I use the #Id annotation, hibernate also logs the correct create table statement.
This is my Ingredient class:
public class Ingredient {
#Id
#Column(name = "ingredient_id")
private String ingredient_id;
#Column(name = "name")
private String name;
#Column(name = "curr_stock")
private double curr_stock;
#Column(name = "opt_stock")
private double opt_stock;
#Column(name = "unit")
private String unit;
#Column(name = "price_per_unit")
private double price_per_unit;
#Column(name = "supplier")
private String supplier;
-- ... getters, setters, constructors (they work fine, I can insert and get the data)
}
My controller looks like this:
#RestController
#RequestMapping(path = "api/v1/ingredient")
public class IngredientController {
private final IngredientService ingredientService;
#Autowired
public IngredientController(IngredientService ingredientService) {
this.ingredientService = ingredientService;
}
#GetMapping
public List<Ingredient> getIngredients(){
return ingredientService.getIngredients();
}
#PostMapping
public void registerNewStudent(#RequestBody Ingredient ingredient) {
ingredientService.saveIngredient(ingredient);
}
}
And my service class just uses the save() method from the JpaRepository to store new ingredients.
To this point I had the feeling, that I understood the whole thing, but when sending two post-requests to my application, each one containing an ingredient with the id "1234", and then showing all ingredients with a get request, the first ingredient just got replaced by the second one and there was no error or smth. like that in between.
Sending direct sql insert statements to the database with the same values throws an error, because the primary key constraint gets violated, just as it should be. Exactly this should have happened after the second post request (in my understanding).
What did I get wrong?
Update:
From the terminal output and the answers I got below, it is now clear, that the save() method can be understood as "insert or update if primary key is already existing".
But is there a better way around this than just error-handle every time when saving a new entry by hand?
The save method will create or update the entry if the id already exists. I'd switch to auto generating the ID when inserting, instead of manually creating the IDs. That would prevent the issue you have
When saving a new ingredient, jpa will perform an update if the value contained in the “id” field is already in the table.
A nice way through which you can achieve what you want is
ingredientRepository.findById(ingredientDTO.getIngredientId()).
ifPresentOrElse( ingredientEntity-> ResponseEntity.badRequest().build(), () -> ingredientRepository.save(ingredientDTO));
You can return an error if the entity is already in the table otherwise (empty lambda), you can save the new row
This is a downside to using CrudRepository save() on an entity where the id is set by the application.
Under the hood EntityManager.persist() will only be called if the id is null otherwise EntityManager.merge() is called.
Using the EntityManager directly gives you more fine grained control and you can call the persist method in your application when required

Hibernate - how to load different view of same object

How is this called and how to solve my next problem on API. I have to return same object with different views. Some data should not be returned to user. Here is example:
Parent:
public class OrginalObject{
private int id;
private String name;
private String surname;
private int age;
private String school;
private String secret;
private Address Address;
child:
public class Address{
private int id;
private String street;
private String zipCode;
private String Country;
If i want to load list of complete objects i would call:
session.createCriteria(OrginalObject.class).list();
1.) But if don't want someone to know my property secret, i need to hide it. But i don't know how to call it from database the way it would have every other property. Something like:
session.createCriteria(OrginalObjectPublic.class).list();
2.) Also I would like to have option to load only "important" data. That means only properties id, name, school.
session.createCriteria(OrginalObjectImportant.class).list();
Is there a way to do an adapter/"custom view" to directly load it from database? I know i can write pure sql, but i would like to use it on objects with 20+ properties that have nested lists/objects.
3.) Also how to use this transformation to load only few properties of nested object with those from orginal. Example json (only id, name, school from OrginalOBject and id, street from Address:
{
"id": 1,
"name": "testname",
"school": "testschool",
"Address": {
{
"id": 33,
"street": "testStreet 33"
}
}
4.) also how to use it on nested Lists if Address would be array:
public class OrginalObject{
...
private List<Address> AddressList;
Since hibernate is a persistence-framework and you can not save/persist to a view, this is not possible. Yes you can make a view having the name like the table and preferr the view but you will not be able to store to that entity anymore.
You can remove the getter (getSecret) from the entity. So the database still have the field but your entity is not aware of it. This may cause problems if you try to store data using that entity, you may not be able to set the secret.
You can make the getter default (package-level-access) and seal the package to let noone else than the sealed projects access the getter.
You can use spring's method authorization mechanism
First no one have access to your secret,
only you the programmer who is supposed to see it.
Second if no one is supposed to have it why store it.
And if you want to pull it out you can use inheritance.
something like
public abstract PublicObject {
...
}
public OriginalObject extends PublicObject {
String secret;
}
Edit:
2nd & 4th questions you can solve them with hql:
String hql = "SELECT O.id, O.name, O.school FROM OrginalObject O";
Query query = session.createQuery(hql);
List results = query.list();
as for your 3th question it depends on your api. if you're using jackson for example you can use #JsonIgnore
You can map more than one entity to the same table, each one with the set of properties you want to expose.
Take a look to this question.

How to beautifully update a JPA entity in Spring Data?

So I have looked at various tutorials about JPA with Spring Data and this has been done different on many occasions and I am no quite sure what the correct approach is.
Assume there is the follwing entity:
package stackoverflowTest.dao;
import javax.persistence.*;
#Entity
#Table(name = "customers")
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private long id;
#Column(name = "name")
private String name;
public Customer(String name) {
this.name = name;
}
public Customer() {
}
public long getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
We also have a DTO which is retrieved in the service layer and then handed to the controller/client side.
package stackoverflowTest.dto;
public class CustomerDto {
private long id;
private String name;
public CustomerDto(long id, String name) {
this.id = id;
this.name = name;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
So now assume the Customer wants to change his name in the webui - then there will be some controller action, where there will be the updated DTO with the old ID and the new name.
Now I have to save this updated DTO to the database.
Unluckily currently there is no way to update an existing customer (except than deleting the entry in the DB and creating a new Cusomter with a new auto-generated id)
However as this is not feasible (especially considering such an entity could have hundreds of relations potentially) - so there come 2 straight forward solutions to my mind:
make a setter for the id in the Customer class - and thus allow setting of the id and then save the Customer object via the corresponding repository.
or
add the id field to the constructor and whenever you want to update a customer you always create a new object with the old id, but the new values for the other fields (in this case only the name)
So my question is wether there is a general rule how to do this?
Any maybe what the drawbacks of the 2 methods I explained are?
Even better then #Tanjim Rahman answer you can using Spring Data JPA use the method T getOne(ID id)
Customer customerToUpdate = customerRepository.getOne(id);
customerToUpdate.setName(customerDto.getName);
customerRepository.save(customerToUpdate);
Is's better because getOne(ID id) gets you only a reference (proxy) object and does not fetch it from the DB. On this reference you can set what you want and on save() it will do just an SQL UPDATE statement like you expect it. In comparsion when you call find() like in #Tanjim Rahmans answer spring data JPA will do an SQL SELECT to physically fetch the entity from the DB, which you dont need, when you are just updating.
In Spring Data you simply define an update query if you have the ID
#Repository
public interface CustomerRepository extends JpaRepository<Customer , Long> {
#Query("update Customer c set c.name = :name WHERE c.id = :customerId")
void setCustomerName(#Param("customerId") Long id, #Param("name") String name);
}
Some solutions claim to use Spring data and do JPA oldschool (even in a manner with lost updates) instead.
Simple JPA update..
Customer customer = em.find(id, Customer.class); //Consider em as JPA EntityManager
customer.setName(customerDto.getName);
em.merge(customer);
This is more an object initialzation question more than a jpa question, both methods work and you can have both of them at the same time , usually if the data member value is ready before the instantiation you use the constructor parameters, if this value could be updated after the instantiation you should have a setter.
If you need to work with DTOs rather than entities directly then you should retrieve the existing Customer instance and map the updated fields from the DTO to that.
Customer entity = //load from DB
//map fields from DTO to entity
So now assume the Customer wants to change his name in the webui -
then there will be some controller action, where there will be the
updated DTO with the old ID and the new name.
Normally, you have the following workflow:
User requests his data from server and obtains them in UI;
User corrects his data and sends it back to server with already present ID;
On server you obtain DTO with updated data by user, find it in DB by ID (otherwise throw exception) and transform DTO -> Entity with all given data, foreign keys, etc...
Then you just merge it, or if using Spring Data invoke save(), which in turn will merge it (see this thread);
P.S. This operation will inevitably issue 2 queries: select and update. Again, 2 queries, even if you wanna update a single field. However, if you utilize Hibernate's proprietary #DynamicUpdate annotation on top of entity class, it will help you not to include into update statement all the fields, but only those that actually changed.
P.S. If you do not wanna pay for first select statement and prefer to use Spring Data's #Modifying query, be prepared to lose L2C cache region related to modifiable entity; even worse situation with native update queries (see this thread) and also of course be prepared to write those queries manually, test them and support them in the future.
I have encountered this issue!
Luckily, I determine 2 ways and understand some things but the rest is not clear.
Hope someone discuss or support if you know.
Use RepositoryExtendJPA.save(entity). Example:
List<Person> person = this.PersonRepository.findById(0)
person.setName("Neo");
This.PersonReository.save(person);
this block code updated new name for record which has id = 0;
Use #Transactional from javax or spring framework. Let put #Transactional upon your class or specified function, both are ok. I read at somewhere that this annotation do a "commit" action at the end your function flow. So, every things you modified at entity would be updated to database.
There is a method in JpaRepository
getOne
It is deprecated at the moment in favor of
getById
So correct approach would be
Customer customerToUpdate = customerRepository.getById(id);
customerToUpdate.setName(customerDto.getName);
customerRepository.save(customerToUpdate);

Spring JPA repository: prevent update on save

My user DB table looks like this:
CREATE TABLE user (
username VARCHAR(32) PRIMARY KEY,
first_name VARCHAR(256) NOT NULL,
last_name VARCHAR(256) NOT NULL,
password VARCHAR(32) NOT NULL,
enabled BOOL
) ENGINE = InnoDB;
This is the field definitions of my entity:
#Entity
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(nullable = false)
private String username;
#Column(nullable = false)
private String firstName;
#Column(nullable = false)
private String lastName;
#Column(nullable = false)
private String password;
The field username is the key of my table/entity and it's up to me to set its value.
When I need to create another user, I do this in my service:
public User insertUserImpl(String username, String firstName, String lastName) {
Assert.hasText(username);
Assert.hasText(firstName);
Assert.hasText(lastName);
String password = UUID.randomUUID().toString().substring(0, 4); // temp
User user = new User(username, password);
user.setFirstName(firstName);
user.setLastName(lastName);
user.setEnabled(false);
this.userRepository.save(user);
// FIXME - assegnare un ruolo
return user;
}
Anyway, if the username is already taken, the repository just do an update, because the specified identifier is not null. This is not the behaviour that I want, I need it to throw something like a duplicate entry exception.
Is there any way to prevent it? Do I have to do it by myself?
E.g.:
User user = this.userRepository.findOne(username);
if(user != null) {
throw new RuntimeException("Username already taken"); // FIXME - eccezione applicativa
}
When using the default configuration, and using CrudRepository#save() or JpaRepository#save() it will delegate to the EntityManager to use either persists() if it is a new entity, or merge() if it is not.
The strategy followed to detect the entity state, new or not, to use the appropiate method, when using the default configuration is as follows:
By default, a Property-ID inspection is performed, if it is null, then it is a new entity, otherwise is not.
If the entity implements Persistable the detection will be delegated to the isNew() method implemented by the entity.
There is a 3rd option, implementing EntityInformation, but further customizations are needed.
source
So in your case, as you are using the username as ID, and it isn't null, the Repository call ends up delegating to EntityManager.merge() instead of persist(). So there are two possible solutions:
use a diferent ID property, set it to null, and use any auto-generation method, or
make User implement Persistable and use the isNew() method, to determine if it is a new entity or not.
If for some reason, you don't want to modify your entities, you can also change the behaviour modifying the flush mode configuration. By default, in spring data jpa, hibernate flush mode is set to AUTO. What you want to do is to change it to COMMIT, and the property to change it is org.hibernate.flushMode. You can modify this configuration by overriding a EntityManagerFactoryBean in a #Configuration class.
And if you don't want to mess the configuration of the EntityManager, you can use the JpaRepository#flush() or JpaRepository#saveAndFlush() methods, to commit the pending changes to the database.
One can perhaps use existsById(ID primaryKey) to test it, if userRepository extends CrudRepository:
if(userRepository.existsById(username)){
//Throw your Exception
} else {
this.userRepository.save(user);
}
see https://docs.spring.io/spring-data/jpa/docs/current/reference/html/
Instead of
this.userRepository.save(user)
, can you try
this.userRepository.saveAndFlush(user)
My best guess is, it will make your entity detached and as per the JPA documentation, it states an EntityExistsException is thrown by the persist method when the object passed in is a detached entity. Or any other PersistenceException when the persistence context is flushed or the transaction is committed.

How annotation mapping is done in java persistence?

We use annotations for mapping the entity class with the database table by simply specifying #Entity and more like #Id, table joins and many things. I do not know how these entity variables are getting mapped with database table. Can anyone give a short description for understanding.
Thanks :)
Well the idea is to translate your objects and their connections with other objects into a relational database. These two ways of representing data (objects defined by classes and in tables in a database) are not directly compatible and that is where a so called Object Relational Mapper framework comes into play.
So a class like
class MyObject
{
private String name;
private int age;
private String password;
// Getters and setters
}
Will translate into a database table containing a column name which is of type varchar, age of type int and password of type varchar.
Annotations in Java simply add additional information (so called meta data) to your class definitions, which can be read by any other class (e.g. JavaDoc) and in the case of the Java Persistence API will be used by an ORM framework like Hibernate to read additional information you need to translate your object into the database (your database table needs a primary id and some information - like what type of a relation an object has to another - can't be automatically determined by just looking at your class definition).
Annotations are very well explained here:
http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/
annotations are just metadata on a class, nothing magical. You can write your own annotations. Those annotations are given retention policies of runtime (which means you have access to that metadata at runtime). When you call persist etc the persistence provider iterates through the fields (java.lang.reflect.Field) in your class and checks what annotations are present to build up your SQL statement. Try writing your own annotation and doing something with it. It won't seem very magical after that.
in your case annotation working means mapping with tablename with entity class is look like as ....
#Entity
#Table(name = "CompanyUser")
public class CompanyUserCAB implements java.io.Serializable
{
private long companyUserID;
private int companyID;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "companyUserID")
public long getCompanyUserID()
{
return this.companyUserID;
}
public void setCompanyUserID(long companyUserID)
{
this.companyUserID = companyUserID;
}
#Column(name = "companyID")
public int getCompanyID()
{
return this.companyID;
}
public void setCompanyID(int companyID)
{
this.companyID = companyID;
}
}

Categories

Resources