Adding and retrieving the data from relational database using spring data jpa - java

I am using spring data jpa
I have two tables person, order
Both have relationship like one to many from person to order
order(person_id) -> person(id)
create table person (id int primary key, name varchar(20));
create table order (id int primary key, name varchar(20), person_id foreign key references person(id));
Consider i have schema in place for both tables and there is no data yet.
I need to insert this data -
Person {name: "person1"}
Order {name: "order1", person_id: <corresponding to person1 record>}
Does inserting the data related by foreign key needs two calls to db?
saving the person
take the primary key from the saved person entity (step 1) then save Order?
Person person = new Person("person1");
Person person = personRepository.save(person);
Order order = new Order("order1");
order.setPersonId(person.getId());
orderRepository.save(order);
Or is there any alternative to save data to two tables using single call to db?
Retrieving the data
If i need to retrieve the person along with orders, will spring data jpa give the result in single db call or need to extract data from two tables separately?
Person person = personRepository.findByName("person1").get(); \\for eg: consider name is unique here
List<Order> orders = orderRepository.findByPersonId(person.getId());
or any alternative in single db call?
Giving clarity to these questions is really appreciated.
Thanks for the answers in advance.

Have a look at cascade types. By adding a cascade type, you could save both objects with just 1 repository call.
#Entity
public class Order {
// other fields...
#OneToMany(mappedBy = "person", cascade = CascadeType.ALL)
private List<Order> orders;
}
#Entity
public class Person {
// other fields...
#ManyToOne
private Person person;
}
Order order = new Order();
Person person = new Person();
person.setOrders(List.of(order));
order.setPerson(person);
personRepository.save(person); // <--- since save action on person cascades, it will also save the order.
Make sure that the objects are linked to each other before saving (the 2 rows above the repository call in the example above)
Regarding fetching data
If you call e.g. the personRepository and it has orders linked to it, you can either access them by configuring eager fetch (not recommended) or by wrapping your method in a Transactional annotation and access the orders programmatically instead.
#Transactional
public void doSomething() {
Person person = personRepository.findById(1);
List<Order> orders = person.getOrders();
}
Note that from a db perspective, in both scenarios with saving and fetching data, the same number of queries will be executed as when calling with separate repositories, but you reduce the code needed to do so and its a bit easier to work inside a transaction and only focus on the java object instead of having to call multiple repositories, especially as your db data model grows.

Related

JPA/HIBERNATE doesn't retrieve correctly entity after its own save

Hi i have straightforward problem,
i want insert new record in table and after this, retrieve this new record with all relationship (list attached ecc..).
In the insertion phase I have some object like WvClsProgetto with only ID into it, to allow Hibernate to correctly insert the foreign keys,
so these objects do not have additional information but only ID.
When i save my entity i want retrieve it, but when i retrieve my entity my VwClsProgetto inside of it is fill with only ID ... and not with all its properties :
But this properties are all null, obviously record in my table have this properties properly valued.
I do save in this way :
MY REPOSITORY CLASS IS:
BASE REPOSITORY IS FROM JPA :
POM SNIPPET :
HELP ME!
You need to cascade the save, otherwise only the root entity will get saved:
#ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private WvClsProgetto wvClsProgetto;
Update
In order to save an entity with an already existing dependency, you have to retrieve it first, so that it is in the persistence context.
VwClsProgetto existingVwClsProgetto = vwClsProgettoRepository.findById(
scheda.getVwClsProgetto().getId())
.get()
then re-set that property:
scheda.setVwClsProgetto(existingVwClsProgetto);

mapping two fields from one table on to other

I have these 2 tables
Users(
id PK,
name VARCHAR(30)
);
The other table is
Orders(
id PK,
orderBy FK Users.id,
orderTo FK Users.id
);
Now, what I want to do is to create Orders entity class which maps orderBy and orderTo to the user. But the most thing i am confuse about is what cascading i should use.
class Orders{
///
#ManyToOne(fetch = FetchType.Lazy
#JoinColumn(name="orderBy")
Users orderBy;
///
#ManyToOne(fetch = FetchType.Lazy
#JoinColumn(name="orderTo")
Users orderTo;
}
I am thinking to create two fields in Users Table such that
class Account{
///
#OneToMany(fetch = FetchType.Lazy)
#JoinColumn(name="orderTo")
List<Orders> ordersReceived;
///
#OneToMany(fetch = FetchType.Lazy)
#JoinColumn(name="orderBo")
List<Orders> ordersPlaced;
}
But again, I am not sure what cascading shall i use. My Users table will be populated by some other processes so orders has nothing to do with. I don't want when i am placing an order, that particular transaction should add/delete anything. HOWEVER, i might need to update a specific field of User whenever i place an order.
I'll suggest to avoid to use cascade at all (if possible)... When you place an order, you should follow the following steps:
1) load your user from your database
2) create your order ...
3) linkup your order to your user (this is, order.setOrderBy(user))
4) persist your order with your EntityManager.
5) Change your user attribute.
From my experience, Cascade should be used carefully. I only used it for persist entities in one shoot (Cascade.PERSIST) (example: persisting a newly user with another new entities like orders)

Spring Data JPA - simulate a "create + join" query for an existing collection

Let's say I have a List of entities:
List<SomeEntity> myEntities = new ArrayList<>();
SomeEntity.java:
#Entity
#Table(name = "entity_table")
public class SomeEntity{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private int score;
public SomeEntity() {}
public SomeEntity(long id, int score) {
this.id = id;
this.score = score;
}
MyEntityRepository.java:
#Repository
public interface MyEntityRepository extends JpaRepository<SomeEntity, Long> {
List<SomeEntity> findAllByScoreGreaterThan(int Score);
}
So when I run:
myEntityRepository.findAllByScoreGreaterThan(10);
Then Hibernate will load all of the records in the table into memory for me.
There are millions of records, so I don't want that. Then, in order to intersect, I need to compare each record in the result set to my List.
In native MySQL, what I would have done in this situation is:
create a temporary table and insert into it the entities' ids from the List.
join this temporary table with the "entity_table", use the score filter and then only pull the entities that are relevant to me (the ones that were in the list in the first place).
This way I gain a big performance increase, avoid any OutOfMemoryErrors and have the machine of the database do most of the work.
Is there a way to achieve such an outcome with Spring Data JPA's query methods (with hibernate as the JPA provider)? I couldn't find in the documentation or in SO any such use case.
I understand you have a set of entity_table identifiers and you want to find each entity_table whose identifier is in that subset and whose score is greater than a given score.
So the obvious question is: how did you arrive to the initial subset of entity_tables and couldn't you just add the criteria of that query to your query that also checks for "score is greater than x"?
But if we ignore that, I think there's two possible solutions. If the list of some_entity identifiers is small (what exactly is "small" depends on your database), you could just use an IN clause and define your method as:
List<SomeEntity> findByScoreGreaterThanAndIdIn(int score, Set<Long) ids)
If the number of identifiers is too large to fit in an IN clause (or you're worried about the performance of using an IN clause) and you need to use a temporary table, the recipe would be:
Create an entity that maps to your temporary table. Create a Spring Data JPA repository for it:
class TempEntity {
#Id
private Long entityId;
}
interface TempEntityRepository extends JpaRepository<TempEntity,Long> { }
Use its save method to save all the entity identifiers into the temporary table. As long as you enable insert batching this should perform all right -- how to enable differs per database and JPA provider, but for Hibernate at the very least set the hibernate.jdbc.batch_size Hibernate property to a sufficiently large value. Also flush() and clear() your entityManager regularly or all your temp table entities will accumulate in the persistence context and you'll still run out of memory. Something along the lines of:
int count = 0;
for (SomeEntity someEntity : myEntities) {
tempEntityRepository.save(new TempEntity(someEntity.getId());
if (++count == 1000) {
entityManager.flush();
entityManager.clear();
}
}
Add a find method to your SomeEntityRepository that runs a native query that does the select on entity_table and joins to the temp table:
#Query("SELECT id, score FROM entity_table t INNER JOIN temp_table tt ON t.id = tt.id WHERE t.score > ?1", nativeQuery = true)
List<SomeEntity> findByScoreGreaterThan(int score);
Make sure you run both methods in the same transaction, so create a method in a #Service class that you annotate with #Transactional(Propagation.REQUIRES_NEW) that calls both repository methods in succession. Otherwise your temp table's contents will be gone by the time the SELECT query runs and you'll get zero results.
You might be able to avoid native queries by having your temp table entity have a #ManyToOne to SomeEntity since then you can join in JPQL; I'm just not sure if you'll be able to avoid actually loading the SomeEntitys to insert them in that case (or if creating a new SomeEntity with just an ID would work). But since you say you already have a list of SomeEntity that's perhaps not a problem.
I need something similar myself, so will amend my answer as I get a working version of this.
You can:
1) Make a paginated native query via JPA (remember to add an order clause to it) and process a fixed amount of records
2) Use a StatelessSession (see the documentation)

How to avoid duplicate records when update in DB using hibernate

I am updating the record with multiple entries in the db at a time.when i try to create another multiple records, duplicate entries are saved in DB.how to prevent records from duplicates entry using hibernate.i want to restrict 2 or more column value should not equal as before
Defining a primary key, or at least a unique constraint, on the table, is the surest way to avoid duplicates. Then your code will throw some useful errors for you to handle transactions properly.
Define your entity object with Unique Constraints
#Entity
#Table(name="user_group",
uniqueConstraints = {#UniqueConstraint(columnNames = {"user_id", "group_id"})})
public class UserGroup implements Serializable
{
User user //This is user model
Group group // This is Group model
// Other fields
// setter and getter methods.
}
Save the objects within a transaction
Session session = sessionFactory().openSession();
session.beginTransaction();
session.saveOrUpdate(listOfuserGroup);
session.getTransaction().commit();

Hibernate : many to many linker table, best OO design

If I have 3 tables, with the expected normal columns : Customer, CustomerProductLinker and Product.
And I want in my Java code to do this :
Customer customer = myService.getCustomer(id); //calls hibernate session
List<Product> customerProducts = customer.getProducts();
What would my 3 entities look like and the respective collections within each, specifically the getProducts() method ? Or is it better to use HQL and a named query for this ?
I am creating the databse tables from the java code (using the create option in hibernate conf), so the table desgin can be altered if preferred.
Try #ManyToMany relationship using #JoinTable. A customer has a set (or a list) of products. A product has a set (or a list) of customers.
#Entity
public class Customer {
#ManyToMany(cascade= CascadeType.ALL)
#JoinTable(name="customer_product",
joinColumns={#JoinColumn(name="customer_id")},
inverseJoinColumns={#JoinColumn(name="product_id")})
private Set<Product> products = new HashSet<Product>();
...
#Entity
public class Product {
#ManyToMany
#JoinTable(name="customer_product",
joinColumns={#JoinColumn(name="product_id")},
inverseJoinColumns={#JoinColumn(name="customer_id")})
private Set<Customer> customers = new HashSet<Customer>();
...
I would set up the entities like wannik suggested. Try to keep it simple. If you start using named queries you are doing more work and you are just covering an specific case.

Categories

Resources