#SecondaryTable with where condition - spring

I am creating entity for table created outside of my system. I want to bind data from other table to entity field by using #SecondaryTable (or possibly better solution), but only to do so if condition is met. IE. my table has 1 row, I want to bind data from other table (oneToMany) where certain condition is met (exactly one match from other table(transform to one to one)). Can I use #Where annotation and how? If not is there alternative?
Edit: here is the entity and additional info on the related table
#Entity
#Table(name = "RE_STORAGE_INSTANCE")
public class Movie {
#Id
#Column(name="ID_")
private Long id;
...
//Column I want to fetch
private Date dueDate;
}
Table RE_VARIABLES manyToOne to table RE_STORAGE_INSTANCE, contains fields: re_key, re_value. I want to fetch re_value only if 're_key' equals dueDate. Even though it's manyToOne, only one row of RE_VARIABLES contains due date for each RE_STORAGE_INSTANCE row.

Related

spring data jpa + unwanted unique key generation

I am using spring data jpa for creation of my tables. My Requirement.
I have two Tables.
Basket Table - It has one to many Relationship with the Item table. A basket can have many Items.
Item Table - Here an Item can be associated with many Baskets.
I am using IGNORE_ROW_ON_DUPKEY_INDEX to make sure that the same combination of basketId and itemId, does not get persisted
So there is a mapping table in between in which holds the mapping of baskets and items. Now, in this table, i want the combination of basketId and itemId to be unique. Below is my entity stucture.
#Entity
Class Basket{
#Id
private long basketId;
...
#OneToMany(cascade = {CascadeType.Merge, CascadeType.DETACH, CascadeType.REFRESH})
#JoinTable(
name= "mapping_table",
joinColumns = #JoinColumn(name ='basketId'),
inverseJoinColumns = #JoinColumn(name ='itemId'),
indexes = {#Index(name = "my_index", columnList = "basketId, itemId", unique = true)}
#SQLInsert(sql = "INSERT /*+ IGNORE_ROW_ON_DUPKEY_INDEX (mapping_table, my_index) */ INTO mapping_table(basketId, itemId) values (?,?)")
private List<Item> itemList;
...
}
#Entity
Class Item{
#Id
private long itemId;
}
my_index with the combination of both the keys are getting created, as expected in the mapping_table
Problem 1:
In the mapping_table, for some wierd reason, a new unique constraint with only itemId is created. Due to this unique key constraint, i am not able to persist rows where an item is associated with multiple baskets. As i said, i want the combination of both the keys to be unique and i am achieving this by creating the index (my_index)
Problem 2:
Why is basketId (which is also Identifier) in the basketTable not marked as unique in the mapping table. This not a problem but more of a question. Becuase itemId which is identifier in the item table has unique key constraint in the mapping table.
Solution :
I create the tables using spring data jpa, login to the db manually and drop the unique key constraint and the persist. But this is not possible in Production
Question
I want to do alter table to drop the constraint first before the persist thing happens. How to do that.
P.S, As you can see, I have imagined these tables and have not put the names of the actual table. Not withstanding the made up table names, the problem statement is accurate. I am using Oracle as target DB
Try to use a Set instead of a List, JPA should generated the correct schema with the correct constraints.

Spring Data / Hibernate save entity with Postgres using Insert on Conflict Update Some fields

I have a domain object in Spring which I am saving using JpaRepository.save method and using Sequence generator from Postgres to generate id automatically.
#SequenceGenerator(initialValue = 1, name = "device_metric_gen", sequenceName = "device_metric_seq")
public class DeviceMetric extends BaseTimeModel {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "device_metric_gen")
#Column(nullable = false, updatable = false)
private Long id;
///// extra fields
My use-case requires to do an upsert instead of normal save operation (which I am aware will update if the id is present). I want to update an existing row if a combination of three columns (assume a composite unique) is present or else create a new row.
This is something similar to this:
INSERT INTO customers (name, email)
VALUES
(
'Microsoft',
'hotline#microsoft.com'
)
ON CONFLICT (name)
DO
UPDATE
SET email = EXCLUDED.email || ';' || customers.email;
One way of achieving the same in Spring-data that I can think of is:
Write a custom save operation in the service layer that
Does a get for the three-column and if a row is present
Set the same id in current object and do a repository.save
If no row present, do a normal repository.save
Problem with the above approach is that every insert now does a select and then save which makes two database calls whereas the same can be achieved by postgres insert on conflict feature with just one db call.
Any pointers on how to implement this in Spring Data?
One way is to write a native query insert into values (all fields here). The object in question has around 25 fields so I am looking for an another better way to achieve the same.
As #JBNizet mentioned, you answered your own question by suggesting reading for the data and then updating if found and inserting otherwise. Here's how you could do it using spring data and Optional.
Define a findByField1AndField2AndField3 method on your DeviceMetricRepository.
public interface DeviceMetricRepository extends JpaRepository<DeviceMetric, UUID> {
Optional<DeviceMetric> findByField1AndField2AndField3(String field1, String field2, String field3);
}
Use the repository in a service method.
#RequiredArgsConstructor
public class DeviceMetricService {
private final DeviceMetricRepository repo;
DeviceMetric save(String email, String phoneNumber) {
DeviceMetric deviceMetric = repo.findByField1AndField2AndField3("field1", "field", "field3")
.orElse(new DeviceMetric()); // create new object in a way that makes sense for you
deviceMetric.setEmail(email);
deviceMetric.setPhoneNumber(phoneNumber);
return repo.save(deviceMetric);
}
}
A word of advice on observability:
You mentioned that this is a high throughput use case in your system. Regardless of the approach taken, consider instrumenting timers around this save. This way you can measure the initial performance against any tunings you make in an objective way. Look at this an experiment and be prepared to pivot to other solutions as needed. If you are always reading these three columns together, ensure they are indexed. With these things in place, you may find that reading to determine update/insert is acceptable.
I would recommend using a named query to fetch a row based on your candidate keys. If a row is present, update it, otherwise create a new row. Both of these operations can be done using the save method.
#NamedQuery(name="getCustomerByNameAndEmail", query="select a from Customers a where a.name = :name and a.email = :email");
You can also use the #UniqueColumns() annotation on the entity to make sure that these columns always maintain uniqueness when grouped together.
Optional<Customers> customer = customerRepo.getCustomersByNameAndEmail(name, email);
Implement the above method in your repository. All it will do it call the query and pass the name and email as parameters. Make sure to return an Optional.empty() if there is no row present.
Customers c;
if (customer.isPresent()) {
c = customer.get();
c.setEmail("newemail#gmail.com");
c.setPhone("9420420420");
customerRepo.save(c);
} else {
c = new Customer(0, "name", "email", "5451515478");
customerRepo.save(c);
}
Pass the ID as 0 and JPA will insert a new row with the ID generated according to the sequence generator.
Although I never recommend using a number as an ID, if possible use a randomly generated UUID for the primary key, it will qurantee uniqueness and avoid any unexpected behaviour that may come with sequence generators.
With spring JPA it's pretty simple to implement this with clean java code.
Using Spring Data JPA's method T getOne(ID id), you're not querying the DB itself but you are using a reference to the DB object (proxy). Therefore when updating/saving the entity you are performing a one time operation.
To be able to modify the object Spring provides the #Transactional annotation which is a method level annotation that declares that the method starts a transaction and closes it only when the method itself ends its runtime.
You'd have to:
Start a jpa transaction
get the Db reference through getOne
modify the DB reference
save it on the database
close the transaction
Not having much visibility of your actual code I'm gonna abstract it as much as possible:
#Transactional
public void saveOrUpdate(DeviceMetric metric) {
DeviceMetric deviceMetric = metricRepository.getOne(metric.getId());
//modify it
deviceMetric.setName("Hello World!");
metricRepository.save(metric);
}
The tricky part is to not think the getOne as a SELECT from the DB. The database never gets called until the 'save' method.

JPA Enitiy with one columns joined in a relationship for two other entities

Currently I'm trying to implement several entities of which one has a column in database which can represent both of other two entities.
To make it clear these are the tables I'm talking about and I can not change them
Images table:
Users table is pretty big, but part of it should be enough:
And pages table:
the logic is that image can be owned by user or by page. who it is owned by indicates by position_id and path columns. so path can be either USER or PAGE, and position_id is user's/page's id.
How can I implement ManyToOne relation between position_id and user and page in JPA?
I imageined it would look something like this:
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name="position_id", insertable = false, updatable = false)
#Where(clause="path='pages'")
private Page page;
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name="position_id", insertable = false, updatable = false)
#Where(clause="path='users'")
private User user;
However this returns all image instances for user or page, disregarding what path it has. Furthermore, when I try to filter the value depending on Image entity's path value, it gives me LazyInitializationException.
How can one go about in solving this problem?

JDBC: select entities with Many to one relation

I have the two entity classes with bi-directional Many-to-one relation.
class A {
#Column(name="ID")
long Id;
}
class B {
#ManyToOne
#JoinColumn(name="A_ID")
A a;
}
The entities are well-coded with additional data fields and getters and setters. And now I want to construct a query string to fetch data from table B, where B's "A_ID" column is equal to A's "ID".
I tried something like this:
"select b.data1, b.data2 from B b, A a WHERE b.a.Id=a.Id"
But it does not work. What is the correct way to construct such a query? And if A and B are in a uni directional relation, would there be any difference?
Thanks in advance.
You don't need to join the tables, the whole idea behind #ManyToOne and #OneToMany is to do away with the need for most joins.
I refer you to a tutorial on JPA, like http://en.wikibooks.org/wiki/Java_Persistence/ManyToOne and http://en.wikibooks.org/wiki/Java_Persistence/OneToMany.
Now, without seeing your actual db definitions it's a bit difficult to guess the actual structure of your program and database, but it should be something like this:
class A {
#Id
#Column(name="ID")
long Id;
#OneToMany(mappedBy="a")
List<B> bees;
}
class B {
#ManyToOne
#JoinColumn(name="A_ID") // Note that A_ID is a column in the B table!!!
A a;
}
With the example above you could just select any list of B's you need, and JPA will automatically fetch the associated A for each found B. You don't need to do anything to be able to access it, b.a.Id will just work.
As we also have the OneToMany relationship, every A can have multiple B's associated with it. So, for any select that fetches a set of A's, each returned A's bees field will give access to the proper list of B objects, without the need to pull the B able into the query.

Retrieving rows based on a certain criteria regarding a many-to-many mapping in Hibernate

I'm just copy & pasting some of the introductory text from one of my questions, since the same table relationship is involved in this question also.
I have three of many tables in Oracle (10g) database as listed below. I'm using Hibernate Tools 3.2.1.GA with Spring version 3.0.2.
Product - parent table
Colour - parent table
ProductColour - join table - references colourId and prodId of Colour and Product tables respectively
Where the table ProductColour is a join table between Product and Colour. As the table names imply, there is a many-to-many relationship between Product and Colour which is mapped by PrductColour. I think, the relationship in the database can easily be imagined and is clear with only this much information. Therefore, I'm not going to explore this relationship at length unnecessarily.
An entity (row) in Product is associated with any number entities in Colour and an entity (row) in Colour can also be associated with any number of entities in Product.
Since, it is a many-to-many relationship, it is mapped in the Product and the Colour entity classes (POJOs) with their respective java.util.Set and no direct POJO class for the product_colour table is available.
The class Product looks like the following.
public class Product implements java.io.Serializable
{
private BigDecimal prodId;
private Set<Colour> colours = new HashSet<Colour>(0);
.
.
.
//Other properties with setters and getters.
}
The class Colour looks like the following.
public class Colour implements java.io.Serializable
{
private BigDecimal colourId;
private Set<Product> products = new HashSet<Product>(0);
.
.
.
//Other properties with setters and getters.
}
The actual mapping between entities is available in xxx.hbm.xml files, regarding this question which is unnecessary, I think .
What I want to do is to retrieve only those rows from the Colour table which don't match the colour rows in the ProductColour table for a particular product at a time. In this regard, the native Oracle SQL statement would look something like the following.
SELECT colour_id, colour_name, colour_hex
FROM colour
WHERE colour_id not in (SELECT colour_id FROM product_colour WHERE prod_id=81)
ORDER BY colour_id DESC
Where prod_id can be any valid BigDecimal number in Java which is dynamic.
As noted earlier, the relationship is available as a many-to-many relationship in Hibernate, no POJO class for the database table product_colour is available and therefore, I'm fumbling in writing such an HQL statement in Hibernate. I have tried to write such an HQL statement but no attempts were succeeded.
[The code presented in the rest of the part may completely be unnecessary to review]
I'm therefore following a traditional way. What I'm doing is... I'm first retrieving a single product row from the Product the entity class based on a dynamic value of prodId such as,
List<Product>list=session.createQuery("from Product where prodId=:prodId")
.setParameter("prodId", prodId).list();
and then using a loop, I'm getting the entire Colour set - java.util.Set corresponding to the product_colour table in Oracle which is available in the Product entity for this product such as,
Set<Colour>colours=new HashSet<Colour>(0);
for(Product p:list)
{
if(p!=null)
{
colours=p.getColours();
}
}
As can be seen, the colours Set is being populated with all of the colour rows available (reference rows) in the product_colour table in Oracle.
After getting all of these rows, I'm getting the entire Colour entity class itself (all the row in it) that corresponds to the colour table in Oracle and then removing those rows which match the rows retrieved from the product_colour Oracle table (available in the colours Set in the preceding snippet) satisfying the condition as mentioned earlier such as,
List<Colour>colourList=session.createQuery("from Colour order by colourId desc").list();
Iterator<Colour>it=colourList.iterator();
while(it.hasNext())
{
Colour c=(Colour)it.next();
for(Colour pc:colours) //colours is available in the preceding snippet.
{
if(c==pc)
{
it.remove();
}
}
}
This can do what is intended but doing so, may imply some overhead on the system. Additionally, what I want to achieve doesn't seem possible with this approach which is pagination. I can't use the setFirstResult(int) and the setMaxResults(int) methods to accomplish the task of pagination which is the case otherwise like the one shown below regarding the Product entity class,
List<Product> products=session.createQuery("from product order by prodId desc")
.setMaxResults(0).setFirstResult(4);
So the question is again, regarding this relationship, is this possible to write such an HQL statement that can retrieve only those rows from the Colour entity class which don't match the colour rows in the product_colour Oracle table like the native SQL statement shown above?
How can I achieve the concept of pagination otherwise (in case, it is not possible)?
Short answer to a veeeeery long question:
select colour from Colour colour
where colour.id not in (
select colour2.id from Product product
inner join product.colours colour2
where product.id = :productId)

Resources