Code Monkey home page Code Monkey logo

npjt-extra's Introduction

npjt-extra

Build Status Maven Central

A small layer over Spring's NamedParameterJdbcTemplate, it provide a similar interface as:

With some extra goodies. Requires java8 or later.

Note: this project predates spring-data-jdbc.

See the Use and examples section.

Download

Maven

<dependency>
	<groupId>ch.digitalfondue.npjt-extra</groupId>
	<artifactId>npjt-extra</artifactId>
	<version>2.0.4</version>
</dependency>

Gradle

compile "ch.digitalfondue.npjt-extra:npjt-extra:2.0.4"

Use and examples

npjt-extra is composed of 3 parts:

  • a default RowMapper (annotation based)
  • an interface based query repository
  • the configuration classes

RowMapping definition

For mapping a row to a class, npjt-extra offer a default row mapper that require the following restriction:

  • the class must have only one public constructor (this restriction could be lifted off).
  • each of the constructor argument must have a @Column annotation that map the column name to the parameter.

This constructor approach is for promoting an immutable model.

IF you cannot map using this strategy, you can specify the mapper class in the @Query and @QueryOverride annotation. Your class must implement org.springframework.jdbc.core.RowMapper<T>. See below in the "Basic use" section.

Example:

import ch.digitalfondue.npjt.ConstructorAnnotationRowMapper.Column;

public static class Conf {

  final String key;
  final String value;

  public Conf(@Column("CONF_KEY") String key, @Column("CONF_VALUE") String value) {
    this.key = key;
    this.value = value;
  }
}

As you can probably guess, this class will map a ResultSet containing 2 column named "CONF_KEY" and "CONF_VALUE".

Query repository definition

npjt-extra generate a proxy from the interface defined by the user.

The rules are simple:

  • you can define a method without parameter that return NamedParameterJdbcTemplate: the proxy will return the underlying NamedParameterJdbcTemplate.
  • you can define default methods
  • all the others methods must contain the @Query(...) annotation

Basic use

A basic "query repository" will be similar to (using the Conf class defined before):

import java.util.List;
import ch.digitalfondue.npjt.Bind;
import ch.digitalfondue.npjt.Query;

public interface MySimpleQueries {

  /** insert a key,value pair, return the number of affected rows */
  @Query("INSERT INTO LA_CONF(CONF_KEY, CONF_VALUE) VALUES(:key, :value)")
  int insertValue(@Bind("key") String key, @Bind("value") String value);

  /** 
   * find a single element, as the underlying NamedParameterJdbcTemplate, 
   * it will launch an exception if there are 0 or more than 1 object
   */
  @Query("SELECT * FROM LA_CONF WHERE CONF_KEY = :key")
  Conf findByKey(@Bind("key") String key);

  /** It will map multiple values too */
  @Query("SELECT * FROM LA_CONF")
  List<Conf> findAll();
  
  
  /** You can override the default mapper and specify your own */
  @Query(value = "SELECT * FROM LA_CONF", mapper = MyConfMapper.class)
  List<MyConf> findAllCustomMapper();
  
  
  /** You can search "simple" types too if they are supported by spring jdbc */
  @Query("SELECT CONF_VALUE FROM LA_CONF WHERE CONF_KEY = 'BLA'")
  String findBla();

  /** You can search Lists of "simple" types too if they are supported by spring jdbc */
  @Query("SELECT CONF_KEY FROM LA_CONF")
  List<String> findAllKeys();

}
 

The custom mapper specified in the annotation of the method findAllCustomMapper must implement org.springframework.jdbc.core.RowMapper<T>.

Query override

If you want to support multiple DB which have some slightly different syntax you can override a query:

For example:

import ch.digitalfondue.npjt.Query;
import ch.digitalfondue.npjt.QueryOverride;
import ch.digitalfondue.npjt.QueriesOverride;

public interface QueryTest {
  @Query("SELECT * FROM LA_CONF")
  @QueriesOverride({
  	@QueryOverride(db = "MYSQL", value = "SELECT * FROM LA_CONF_MYSQL"),
  	@QueryOverride(db = "PGSQL", value = "SELECT * FROM LA_CONF_PGSQL")
  })
  List<Conf> findAll();
}

When creating the ch.digitalfondue.npjt.QueryFactory that will generate the repositories, you must specify 2 parameters: first the DB name and second the DataSource.

If the db name match the db parameter specified in a @QueryOverride, the associated String value will be used.

Note: if you are defining a custom mapper in the @Query annotation, you must specify it in the @QueryOverride too!

Fetch generated keys

If you have a table with an auto generated key and you want to get the value after your insert, you can set the return type to AffectedRowCountAndKey<T>.

Example:

You have a table (HSQLDB) defined as:

CREATE TABLE LA_AUTO (ID INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY NOT NULL, VALUE CLOB NOT NULL);

In your QueryRepository you define:

public interface AutogeneratedKeyQueries {

  @Query("INSERT INTO LA_AUTO(VALUE) VALUES (:value)")
  AffectedRowCountAndKey<Integer> insert(@Bind("value") String value);

}

The returned AffectedRowCountAndKey will contain the ID of the inserted element. You can see in action in the test AutogeneratedKeyQueriesTest.java

Notes:

  • in some cases (for example if you are using pgsql), you must define the column name of the generated identifier with the annotation @AutoGeneratedKey("COLUMN_NAME") as the DB even though there is a single generated key it will return more than one.
  • it's currently a basic implementation that support the simplest use cases: pull request or test cases that highlight a missing feature are welcome!

Query templates

If you only require to generate a query string which depend from the db type, you can define a query template.

You need to define the type in the @Query annotation to QueryType.TEMPLATE and the return type of the query as String.

For example:

public interface QueryTest {

  @Query(type = QueryType.TEMPLATE, value = "MY_TEMPLATE")
  @QueriesOverride({
  	@QueryOverride(db = "MYSQL", value = "SELECT * FROM MY_TEMPLATE_MYSQL"),
  	@QueryOverride(db = "PGSQL", value = "SELECT * FROM MY_TEMPLATE_PGSQL")
  })
  String template();
}

Calling template() will return "MY_TEMPLATE" (or the overridden values).

Optional

You can wrap the returned object in a Optional. For example:

@Query("SELECT * FROM LA_CONF WHERE CONF_KEY = :key")
Optional<Conf> findByKey(@Bind("key") String key);

Will work as expected. If the query return more than one object it will launch an exception like the unwrapped version.

Default methods in the interface

You can add default methods too, for example, if you need some custom query directly with the NamedParameterJdbcTemplate:

public interface MySimpleQueries {

  
  @Query("INSERT INTO LA_CONF(CONF_KEY, CONF_VALUE) VALUES(:key, :value)")
  int insertValue(@Bind("key") String key, @Bind("value") String value);
  
  /** any method that return NamedParameterJdbcTemplate and has 0 arguments will return the underlying NamedParameterJdbcTemplate*/
  NamedParameterJdbcTemplate getNamedParameterJdbcTemplate();

  /** here your default method */
  default String defaultMethod(String key) {
    return getNamedParameterJdbcTemplate()
			.queryForObject("SELECT CONF_VALUE FROM LA_CONF WHERE CONF_KEY = :key", Collections.singletonMap("key", key), String.class);
  }
}
LocalDate, LocalDateTime, Instant support

npjt-extra support out of the box LocalDate, LocalDateTime and Instant both as a parameter of a interface method and as a mapped value.

Configuration

You can configure it in 2 ways.

Using @EnableNpjt annotation

First you need to annotate your query repository with the ch.digitalfondue.npjt.QueryRepository annotation.

Note, you will need a DataSource configured.

Then you only need to configure the packages to scan:

/** scan the packages "ch.digitalfondue.npjt.query" and "ch.digitalfondue.npjt.columnmapper" */
@EnableNpjt(basePackages = {"ch.digitalfondue.npjt.query", "ch.digitalfondue.npjt.columnmapper"})
public class MyConfig2 {
}

Manual instantiation

Using the JavaConfig:

public class MyConfig {
   
  /** instantiate the interface MySimpleQueries */
  /** the db type could be another parameter :) */
  @Bean
  public MySimpleQueries getMySimpleQueries(DataSource dataSource) {
    return QueryFactory.from(MySimpleQueries.class, "HSQLDB", dataSource);
  }
  
}

All the annotated interfaces will be available in your spring context.

Data type mapping

By default, npjt-extra has the following input parameters/result set mapping facilities enabled:

  • a default parameter/result set mapper which use the same logic as the one from the jdbctemplate
  • a enum mapper that convert from/to a string representation
  • support ZonedDateTimeMapper, LocalDate, LocalDateTime and Instant

You can add new mappers by exposing as a bean a List<ColumnMapperFactory> and a List<ParameterConverter> (see example at: https://github.com/digitalfondue/npjt-extra/blob/master/src/test/java/ch/digitalfondue/npjt/query/CustomJSONQueriesTest.java#L125).

If you want to configure from an empty list, you can define your custom QueryFactory: see full example at https://github.com/digitalfondue/npjt-extra/blob/master/src/test/java/ch/digitalfondue/npjt/query/customfactory/CustomJSONQueriesWithCustomQueryFactoryTest.java#L131 where instead of calling super.getDefaultFactories(); you can begin from an empty List.

Then when using the @EnableNpjt you will need to specify the Factory:

@EnableNpjt(queryFactory = CustomQueryFactory.class, basePackages = {"ch.digitalfondue.npjt.query.customfactory"})

For both mappers, the application order is defined by the order() method. The smallest the int returned, the higher the priority.

Input parameters mapping

You must implement the ch.digitalfondue.npjt.mapper.ParameterConverter interface and register the converter in the queryFactory calling the QueryFactory.addParameterConverters(ParameterConverter parameterConverter) method.

Result set objects mapping

You must implement the ch.digitalfondue.npjt.mapper.ColumnMapperFactory interface and register the converter in the queryFactory calling the addColumnMapperFactory(ColumnMapperFactory columnMapperFactory) method.

Javadoc

License

The library is under The Apache Software License, Version 2.0

npjt-extra's People

Contributors

cbellone avatar dependabot[bot] avatar syjer avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

npjt-extra's Issues

bump min version to java 8

So we can remove (most?) of the reflection based work.

  • bump java 8
  • cleanup optional reflection tricks
  • check others cleanup
  • make the QueryRepositoryScanner activated through an annotation
  • let the user able to define their custom QueryFactory (in the annotation)

Add support for UUID

Some Databases (like PostgreSQL) support UUID as type.
We should map that type to java.util.UUID.

support a method factory strategy too

When using auto-value, the constructor is not available. So supporting an annotated static method factory inside the target class could be useful.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.