DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
Building Scalable Real-Time Apps with AstraDB and Vaadin
Register Now

Trending

  • 5 Key Concepts for MQTT Broker in Sparkplug Specification
  • Microservices Decoded: Unraveling the Benefits, Challenges, and Best Practices for APIs
  • Building and Deploying Microservices With Spring Boot and Docker
  • Managing Data Residency, the Demo

Trending

  • 5 Key Concepts for MQTT Broker in Sparkplug Specification
  • Microservices Decoded: Unraveling the Benefits, Challenges, and Best Practices for APIs
  • Building and Deploying Microservices With Spring Boot and Docker
  • Managing Data Residency, the Demo
  1. DZone
  2. Data Engineering
  3. Databases
  4. Augmenting a Spring Data Repository Through Delegation

Augmenting a Spring Data Repository Through Delegation

Dan Newton user avatar by
Dan Newton
·
Nov. 22, 19 · Tutorial
Like (2)
Save
Tweet
Share
15.94K Views

Join the DZone community and get the full member experience.

Join For Free

meadow-in-spring

I have recently written several posts about Kotlin’s delegation. In doing so, I realized a useful way to apply it to Spring Data repositories. This would allow Spring Data to continue sprinkling some magic, while providing a route for customization.

The code shown in this post is in Kotlin, but is still relevant to Java. This post uses R2DBC, but the content is generic enough to be applicable to any Spring Data module.

Reading Asynchronous RDBMS access with Spring Data R2DBC and Class delegation in Kotlin would be beneficial here if you do not have much background knowledge in these areas.

You may also like: Using the Spring Data JPA.

What Is the Magic That Spring Data Provides?

Spring Data allows you to write an interface where you are only required to define the queries that you need. It will then do all the work of creating the implementation and injecting dependencies for you. This looks something like:

@Repository
interface PersonRepository : R2dbcRepository<Person, Int> {

  @Query("SELECT * FROM people WHERE age > $1")
  fun findAllByAgeGreaterThan(age: Int): Flux<Person>
}


Since Spring Data R2DBC is being used, fully inferred queries are not yet fully supported. This is why the query is written out manually.

The downside to this is that it is creating an implementation based on an interface. Therefore, if you want to do any sort of customization, you will need to create an instance of the interface yourself, inject in its dependencies, and implement each query. For example:

class PersonRepositoryImpl(
  private val entity: RelationalEntityInformation<Person, Int>,
  private val databaseClient: DatabaseClient,
  converter: R2dbcConverter,
  private val accessStrategy: ReactiveDataAccessStrategy
) : SimpleR2dbcRepository<Person, Int>(entity, databaseClient, converter, accessStrategy),
  PersonRepository {

  override fun findAllByAgeGreaterThan(age: Int): Flux<Person> {

    val mapper: StatementMapper.TypedStatementMapper<Person> =
      accessStrategy.statementMapper.forType(entity.javaType)

    val selectSpec: StatementMapper.SelectSpec = mapper
      .createSelect(entity.tableName)
      .withProjection(accessStrategy.getAllColumns(entity.javaType))
      .withCriteria(Criteria.where("age").greaterThan(age))

    val operation: PreparedOperation<*> = mapper.getMappedObject(selectSpec)

    return databaseClient.execute().sql(operation).`as`(entity.javaType).fetch().all()
  }
}


Yes, that query code is probably terrible, and I am sure you could do better. You get my point though.

The pain of creating this class can be removed by delegating to the repository Spring implemented based on your interface. You can then add all the customizations you need.

In Kotlin, this would look like:

@Repository
class DelegatingPersonRepository(private val delegate: PersonRepository) :
  PersonRepository by delegate {

  override fun <S : Person> save(objectToSave: S): Mono<S> {
    // override `save` implementation
  }

  // any other overrides (kotlin provides delegated implementations)
}


In Java, it is a bit more cumbersome but still easily achievable:

@Repository
public class DelegatingPersonRepository implements PersonRepository {

  private final PersonRepository delegate;

  public DelegatingPersonRepository(PersonRepository delegate) {
    this.delegate = delegate;
  }

  @Override
  public Flux<Person> findAllByAgeGreaterThan(int age) {
    return delegate.findAllByAgeGreaterThan(age);
  }

  @Override
  public <S extends Person> Mono<S> save(S entity) {
    // override `save` implementation
  }

  // all other implementations of `PersonRepository` functions
}


In both versions, DelegatingPersonRepository calls the implementation of findAllByAgeGreaterThan defined in PersonRepository . So far, no effort has been directly spent on writing a function to query a database.

When the DelegatingPersonRepository is used, all function calls that are not overridden will delegate to the implementation of PersonRepository that Spring created.

For someone like me, who doesn’t really like putting together SQL queries and writing all the conversion code, using delegation in this way really allows you to leverage the benefits of Spring Data while still giving you room to customize the outcome. The amount of code you save might not actually be that great. But, there is a considerable reduction in the effort required to put it together. Just let Spring do all the heavy lifting for you!

If you enjoyed this post or found it helpful (or both), then please feel free to follow me on Twitter at @LankyDanDev and remember to share with anyone else who might find this useful!


Further Reading

  • What Is Spring Data JPA?
  • Building A Data Repository With Spring Data.
  • Easy Access to Data With Spring REST Data.
Spring Data Spring Framework Data (computing) Repository (version control) Delegation (object-oriented programming) Database

Published at DZone with permission of Dan Newton, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Trending

  • 5 Key Concepts for MQTT Broker in Sparkplug Specification
  • Microservices Decoded: Unraveling the Benefits, Challenges, and Best Practices for APIs
  • Building and Deploying Microservices With Spring Boot and Docker
  • Managing Data Residency, the Demo

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com

Let's be friends: