Designing one-to-one and one-to-many relationships in Spring Data R2DBC - spring-boot

I am exploring possible ideas when it comes to designing the one-to-one and one-to-many relationships while using Spring Data R2DBC.
As Spring Data R2DBC still do not support relationships natively there is still a need to handle those on our own (unlike Spring Data JDBC).
What I would imagine that when it comes to one-to-one mapping, the implementation could look like this:
#Table("account")
public class Account {
#Id
private Long id;
#Transient // one-to-one
private Address address;
}
#Table("address")
public class Address {
#Id
private Integer id;
}
while the database schema would be defined as follows:
--address
CREATE TABLE address
(
id SERIAL PRIMARY KEY
)
--account
CREATE TABLE account
(
id SERIAL PRIMARY KEY,
address_id INTEGER REFERENCES address(id)
)
As the Account object is my aggregate root what I would imagine is that I am supposed to load the Address object with it following the advice of Jens Schaduer:
An aggregate is a cluster of objects that form a unit, which should
always be consistent. Also, it should always get persisted (and
loaded) together.
source: Spring Data JDBC, References, and Aggregates
This leads me to thinking that in case of one-to-one relationships like this one I in fact should have my Account entity defined like this:
#Table("account")
public class Account {
#Id
private Long id;
#Transient // one-to-one
private Address address;
#Column("address_id")
private Integer addressId;
}
and later on to recreate the full Account aggregate entity with an Address I would write something like:
#Service
public class AccountServiceImpl implements AccountService {
private final AccountRepository accountRepository;
private final AddressRepository addressRepository;
public AccountServiceImpl(AccountRepository accountRepository,
AddressRepository addressRepository) {
this.accountRepository = accountRepository;
this.addressRepository = addressRepository;
}
#Override
public Mono<Account> loadAccount(Integer id) {
return accountRepository.getAccountById(id)
.flatMap(account ->
Mono.just(account)
.zipWith(addressRepository.getAddressByAccountId(account.getAddressId()))
.map(result -> {
result.getT1().setAddress(result.getT2());
return result.getT1();
})
);
}
}
If that is not the case, how else should I handle one-to-one relationships while using Spring Data R2DBC?

I think your approach is reasonable. There are just a couple of nitpicks:
Do you need the flatMap -> Mono.just ? Can't you just use map directly?
I wouldn't consider this a service, but a repository (it's just not implemented by Spring Data directly.
You might be able to that code in a after load callback.

Related

Spring Data Cassandra: How to define a Repository for a table with composite primary key?

This is my table;
#Table
public class Opinion {
private String text;
#PrimaryKeyColumn(type = PrimaryKeyType.PARTITIONED)
private String topic;
#PrimaryKeyColumn(type = PrimaryKeyType.PARTITIONED)
private String owner;
#PrimaryKeyColumn(type = PrimaryKeyType.CLUSTERED)
private LocalDateTime createdDate;
...
}
How can I declare a Repository for this table with a composite primary key? This is what I have now but it does not work, probably because of the ID part, since my id is not just a string but a composite one.
#Repository
public interface OpinionRepository extends CassandraRepository<Opinion, String> {
....
}
By the way I would be glad to hear your experiences whether Cassandra plays nicely with Spring Data or not.. Actually I have been using Spring Data for a while, and I have to say that this time with Cassandra was everything different. Even for some very simple stuff which one can deal with easily on other databases, I have hard time figuring it out. And some of the stuff was not supported at all.. So I am actually very disappointed about Cassandra-Spring Boot interplay.

Spring Data JPA - findBy mapped object

In my legacy application, I have a country table, state table and a mapping table for country and state with few additional columns.
I have created an entity class like this.
class CountryStateMapping {
#Id
private long id;
private Long countryId;
#OneToOne
#JoinColumn(name="state_id")
private State state;
//getters seters
}
My repository.
public interface CountryStateMapping extends JpaRepository<CountryStateMapping, Long>{
Optional<CountryStateMapping> findByStateId(long stateId);
Optional<CountryStateMapping> findByState(State state);
}
I would like to check if the state exists in the mapping table. Both of the below approaches do not work.
countryStateMapping.findByStateId(long stateId)
countryStateMapping.findByState(State state)
What is the right way?
Its not the correct way i feel.The correct way for doing this will be
public interface CountryStateMappingRepository extends JpaRepository<CountryStateMapping, Long> {
Optional<CountryStateMapping> findByStateId(long stateId);
#Query("select s.something from State s" )
Optional<CountryStateMapping> findByState(State state);
}
This implies two things
By extending JpaRepository we get a bunch of generic CRUD methods to create, update, delete, and find
2.It allows Spring to scan the classpath for this interface and create a Spring bean for it.
Also you need some configuration.For that you need to create a configuration class to be used with your data source.You can find many examples to do the same and one such is https://www.baeldung.com/the-persistence-layer-with-spring-data-jpa.
You can also use custom queries and simple queries using the #Query annotation.
Thanks
Try with an underscore for id like below;
public interface CountryStateMapping<CountryStateMapping, Long>{
Optional<CountryStateMapping> findByState_Id(long stateId);
Optional<CountryStateMapping> findByState(State state);
}

how to write the JpaRepository for tables which has composite keys

Please refer attached screenshot to understand the table structure.
Empd_Id is the primary key in 'Employee' table which in turn becomes as a part of composite key along with 'product_id' in table called 'product'.
Any employee can have multiple products so in that case it becomes 'One-to-Many' relationship between 'Employee-Product' tables. Now I'm confused whether I need to write just 1 JpaRepository interface i.e. for employee or 2 JpaRepository interfaces (1 for Employee and another for Product). My gut feeling is just 1 interface for Employee table but how???
Following is my code snippet:-
1st JPA repository interface
public interface MyRepository extends JpaRepository<Product, EmpProd> {
}
Entity:-
#Entity
#Table(name="product")
public class Product{
#EmbeddedId
private EmpProd empProd;
#Column(name="product_name")
private String commerceUserId;
#Column(name="description")
private String description;
For composite keys:-
#Embeddable
public class EmpProd implements Serializable{
private static final long serialVersionUID = 1L;
#NotNull
#Column(name="emp_id")
private String empId;
#NotNull
#Column(name="product_id")
private String productId;
2nd Jpa repository interface
public interface MyMainDataRepository extends JpaRepository<Employee, String> {
}
Entity class:-
#Entity
#Table(name="employee")
public class Employee{
#Id
#NotNull
#Column(name="emp_id")
private String empId;
#Column(name="first_name")
private String firstName;
Though, I have written 2 separate JPA repositories, I strongly believe there will be need for just 1, the main one i.e.
public interface MyMainDataRepository extends JpaRepository {
}
But I do not know to related both entity classes and fetch data from using single Jpa repository as I'm new to Spring Data JPA. I would really appreciate if someone can help me here. Thanks
The two entities Product and Employee don't have any connection as far as JPA is concerned. Therefore you can't access both through a single repository.
If for example, Product would have an actual reference to an Employee you could use a ProductRepository to load Products and navigate from there to the referenced Employees.
But even if that might be feasible, I'd guess that Product and Employee should be considered different aggregates and therefore, should have their own repository each. See Are you supposed to have one repository per table in JPA? for more information on that question.
Given the entities, your repositories look just fine. Note that the entities do look atypical due to the use of String productId instead of Product product.
If you wanted to fetch the employee details, you need the following interface,
public interface MyMainDataRepository extends JpaRepository<Employee, String> {
}
If you wanted to fetch the product details, you need the following interface,
public interface MyRepository extends JpaRepository<Product, EmpProd> {
}
The employee is related to product table, the iteration happens via product and related employees. From this, you can not access the employee table directly and retrieve the employee results from MyRepository interface.

Spring data join mysql entity with collection mongodb

i m doing a project with spring data. I have two table in my schema:
Hu
Movement: this table must contain each movement of hu. In production this table will have a lot of record so i will put the movement data on mongodb databale.
I've read that it is possible to use more datasource. But it is possible to use a mysql datasource and a mongodb datasource? If yes is possible to link HU to movement ( a join ) ? Movement collection have hu_id column.
yes it is possible to use two datasources one is SQL and the other one is NOSQL.
But I feel linking between two entities is not possible and it doesnt sound right.
Anyway, I have tried this approach where
Entity1.java :(SQL entity)
#Entity
#Table(name="ENTITY1")
public class Entity1 implements Serializable{
#Id
private long id;
}
Entity2.java :(NOSQL entity)
#Document(collection="test")
public class Entity2 implements Serializable{
#org.springframework.data.annotation.Id
private long Id;
//storing reference of entity1
#Field("Entity1REF")
private long entity1Id;
}
Entity1Repository :
public interface Entity1Repository extends JpaRepository<Entity1, Long> ()
Entity2Repository :
public interface Entity2Repository extends MongoRepository<Entity2, Long>{
While performing CRUD operations on the entity:
Make use of appropriate repos to perform.
#Autowired
private Entity1Repository entity1Rep;
#Autowired
private Entity2Repository entity2Rep;
public void init(){
Entity1 en1=new Entity1(100);
en1=entity1Rep.save(en1);
Entity2 en2=new Entity2(1000,en1.getId());
entity2Rep.save(en2);
}
Please go through this project tried :
https://github.com/BarathArivazhagan/Spring-MongoDB-Samples/tree/tree/Spring-Data-Mongo-SQL

Multiple Repositories for the Same Entity in Spring Data Rest

Is it possible to publish two different repositories for the same JPA entity with Spring Data Rest?
I gave the two repositories different paths and rel-names, but only one of the two is available as REST endpoint.
The point why I'm having two repositories is, that one of them is an excerpt, showing only the basic fields of an entity.
The terrible part is not only that you can only have 1 spring data rest repository (#RepositoryRestResource) per Entity but also that if you have a regular JPA #Repository (like CrudRepository or PagingAndSorting) it will also interact with the spring data rest one (as the key in the map is the Entity itself).
Lost quite a few hours debugging random load of one or the other. I guess that if this is a hard limitation of spring data rest at least an Exception could be thrown if the key of the map is already there when trying to override the value.
The answer seems to be: There is only one repository possible per entity.
I ended up using the #Subselect to create a second immutable entity and bound that to the second JpaRepsotory and setting it to #RestResource(exported = false), that also encourages a separation of concerns.
Employee Example
#Entity
#Table(name = "employee")
public class Employee {
#Id
Long id
String name
...
}
#RestResource
public interface EmployeeRepository extends PagingAndSortingRepository<Employee, Long> {
}
#Entity
#Immutable
#Subselect(value = 'select id, name, salary from employee')
public class VEmployeeSummary {
#Id
Long id
...
}
#RestResource(exported = false)
public interface VEmployeeRepository extends JpaRepository<VEmployeeSummary, Long> {
}
Context
Two packages in the monolithic application had different requirements. One needed to expose the entities for the UI in a PagingAndSortingRepository including CRUD functions. The other was for an aggregating backend report component without paging but with sorting.
I know I could have filtered the results from the PagingAndSorting Repository after requesting Pageable.unpaged() but I just wanted a Basic JPA repository which returned List for some filters.
So, this does not directly answer the question, but may help solve the underlying issue.
You can only have one repository per entity... however, you can have multiple entities per table; thus, having multiple repositories per table.
In a bit of code I wrote, I had to create two entities... one with an auto-generated id and another with a preset id, but both pointing to the same table:
#Entity
#Table("line_item")
public class LineItemWithAutoId {
#Id
#GeneratedValue(generator = "system-uuid")
#GenericGenerator(name = "system-uuid", strategy = "uuid")
private String id;
...
}
#Entity
#Table("line_item")
public class LineItemWithPredefinedId {
#Id
private String id;
...
}
Then, I had a repository for each:
public interface LineItemWithoutId extends Repository<LineItemWithAutoId,String> {
...
}
public interface LineItemWithId extends Repository<LineItemWithPredefinedId,String> {
...
}
For the posted issue, you could have two entities. One would be the full entity, with getters and setters for everything. The other, would be the entity, where there are setters for everything, but only getters for the fields you want to make public. Does this make sense?

Resources