Skip to content

dansiviter/cdi-repos

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

GitHub Workflow Status Known Vulnerabilities Sonar Coverage Maven Central Java 17+

CDI Repositories

This library is for auto-generating repositories somewhat similar to Jakarta Data, Spring and Micronaut Data projects. However, instead of performing compilation magic (i.e. no source code) or generating queries from method names (i.e. brittle) it takes a transparent approach which is both minimal and easy to debug.

Usage

This uses Annotation Processing to generate an implementation that creates many useful commonly used methods.

Lets get started... first import dependencies...

Maven:

<dependency>
  <groupId>uk.dansiviter.cdi-repos</groupId>
  <artifactId>cdi-repos</artifactId>
  <version>${cdi-repos.version}</version>
</dependency>
<dependency>
  <groupId>uk.dansiviter.cdi-repos</groupId>
  <artifactId>cdi-repos-processor</artifactId>
  <version>${cdi-repos.version}</version>
  <scope>provided</scope> <!-- only needed during compilation -->
  <optional>true</optional>
</dependency>

Gradle:

annotationProcessor('uk.dansiviter.cdi-repos:cdi-repos-processor:${cdi-repos.version}')
implementation('uk.dansiviter.cdi-repos:cdi-repos:${cdi-repos.version}')

Define a repository interface:

/**
 * Trigger annotation processing by using @Repository. If no PersistenceContext is defined it will use a default value.
 */
@Repository
@PersistenceContext(unitName = "myUnit")
public interface MyRepo {
  // methods will go here
}

This will then generate a @ApplicationScoped concrete implementation called MyRepo$impl.java which can then be inspected and debugged.

ℹ️ This generator does not verify the types are correct for the queries therefore, be careful to correctly specify the correct types or there will be potentially compile and runtime exceptions. It's recommended you perform integration tests to verify these are setup correctly.

There are several method types that can be used; first there are bridge methods. These typically 'bridge' between EntityManager methods but there are a few special cases:

  /**
   * This delegates to EntityManager#find(...) and has alternative #get(...), Optionals on return are supported.
   */
  @Transactional
  Optional<MyEntity> find(int id);

  /**
   * Special case: if this has a key this will delegate to EntityManager#merge(...), if not
   * EntityManager#persist(...).
   */
  void save(MyEntity myEntity);

  /**
   * Same as above but will wrapped with a EntityManager#flush(). Returning the entity is supported as is
   * @Transactional on all bridge methods.
   */
  @Transactional
  MyEntity saveAndFlush(MyEntity myEntity);

ℹ️ Methods like #findAll and #deleteAll are purposefully not automatically generated for memory and data loss reasons. If you wish to do this, use the default method mechanism.

Then there are query methods:

  /**
   * This uses the 'myQuery' named or named native query. It defaults to positional parameters. Optionals
   * will use null if empty.
   */
  @Query("myQuery")
  List<MyEntity> myQuery(String param0, OptionalInt param1, @Temporal(TemporalType.TIMESTAMP) Calendar param2);

  /**
   * This will use the parameter name for the named parameters. Stream results are supported.
   */
  @Query(value = "myQueryStream", namedParameters = true)
  Stream<MyEntity> myQueryStream(String queryParam);

  /**
   * If you expect a single result using Query#getSingleResult.
   */
  @Query("mySingleResult")
  MyEntity myOptionalSingleResultQuery(String param);

  /**
   * You can expect zero or one result. If more than one result is returned NonUniqueResultException will be thrown.
   */
  @Query("myOptionalSingleResult")
  Optional<MyEntity> myOptionalSingleResultQuery(String param);

  /**
   * If a void, int or long is used then it will use Query#executeUpdate and return the result if possible.
   * @Transactional is supported on all query methods.
   */
  @Query("myUpdateQuery")
  @Transactional
  int myUpdateQuery(String param);

Finally, there is custom usage of EntityManager:

  /**
   * Expose the EntityManager to be used in the default method. This could be named anything as long as it returns
   * EntityManager.
   */
  EntityManager entityManager();

  /**
   * Then use it...
   */
  @Transactional
  default int customQuery() {
    var em = entityManager();
    var q = em.getCriteriaQueryBuilder().createCriteriaDelete(MyEntity.class);
    return em.createQuery(q).executeUpdate();
  }

About

CDI based JPA repositories

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages