Git Product home page Git Product logo

uroborosql's Introduction

Maven Central GitHub license Javadocs Build Status Coverage Status

uroboroSQL

uroboroSQL

UroboroSQL is a simple SQL execution library that can utilize 2-way-SQL compatible with Java 8.

UroboroSQL mainly adopts a SQL-centered design concept. The concept does not focus on Java to assemble SQL, but is based on an idea of making up for weaknesses of SQL with Java.

From the knowhow acquired through our business, we have enhanced UroboroSQL with functions like partition value support, retrying, filtering customization, and so on. Also, for quality assurance purposes, it features a coverage analysis function available for 2-way-SQL.

There is also REPL which dramatically increases SQL development productivity.

asciicast

for Japanese, see README.ja.md

Installation

for Maven

<dependency>
    <groupId>jp.co.future</groupId>
    <artifactId>uroborosql</artifactId>
    <version>0.26.7</version>
</dependency>
<dependency>
    <groupId>ognl</groupId>
    <artifactId>ognl</artifactId>
    <version>3.1.23</version>
</dependency>

or

<dependency>
    <groupId>jp.co.future</groupId>
    <artifactId>uroborosql</artifactId>
    <version>0.26.7</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-expression</artifactId>
    <version>5.3.20</version>
</dependency>

for Gradle

compile group: 'jp.co.future', name: 'uroborosql', version: '0.26.7'
compile group: 'ognl', name: 'ognl', version: '3.1.23'

or

compile group: 'jp.co.future', name: 'uroborosql', version: '0.26.7'
compile group: 'org.springframework', name: 'spring-expression', version: '5.3.20'

Documentation

https://future-architect.github.io/uroborosql-doc/

Requirement

  • Java 1.8 or later.

Quick start

2Way-SQL

/* department/select_department.sql */

SELECT /* _SQL_ID_ */
  DEPT.DEPT_NO AS DEPT_NO
, DEPT.DEPT_NAME AS DEPT_NAME
FROM
  DEPARTMENT DEPT
WHERE
  1    = 1
/*IF SF.isNotEmpty(dept_no)*/
AND DEPT.DEPT_NO = /*dept_no*/1
/*END*/
/*IF SF.isNotEmpty(dept_name)*/
AND DEPT.DEPT_NAME = /*dept_name*/'sample'
/*END*/
/* department/insert_department.sql */

INSERT
/* _SQL_ID_ */
INTO
  DEPARTMENT
(
  DEPT_NO
, DEPT_NAME
) VALUES (
  /*dept_no*/1
, /*dept_name*/'sample'
)
SqlConfig config = UroboroSQL.builder("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1", "sa", "").build();

try (SqlAgent agent = config.agent()) {
  // SELECT
  List<Map<String, Object>> departments = agent.query("department/select_department").param("dept_no", 1001).collect();

  // INSERT
  int count = agent.update("department/insert_department")
    .param("dept_no", 1001)
    .param("dept_name", "sales")
    .count();
}

DAO Interface

SqlConfig config = UroboroSQL.builder("jdbc:h2:mem:test;DB_CLOSE_DELAY=-1", "sa", "").build();

try (SqlAgent agent = config.agent()) {
  // select
  Department dept =
      agent.find(Department.class, 1001).orElse(null);

  // insert
  Department hrDept = new Department();
  hrDept.setDeptNo(1002);
  hrDept.setDeptName("HR");
  agent.insert(hrDept);

  // update
  hrDept.setDeptName("Human Resources");
  agent.update(hrDept);

  // delete
  agent.delete(hrDept);
}

Sample application

Automated code generation

SQL Formatter

uroboroSQL

We also prepare a SQL formatter useful for development.

License

Released under the MIT License.

uroborosql's People

Contributors

futa23 avatar future-oss avatar hidekisugimoto189 avatar ota-meshi avatar shout-star avatar ymaegawa avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

uroborosql's Issues

Addding SqlFluent#sqlId

I want to set the SQLID when using the SqlAget#queryWith method, but since it does not exist in the current SqlFluent interface, I have acquired SqlContext once in SqlAgent#contextWith.

It is nice to be able to specify SQLID with SqlFluent#sqlId method.

Entity support for executing batch sql

I want to execute batch sql using entity, but it isn't exists.
Please add entity support for executing batch sql.

// For insert
int insertBatch(List<?> entityList);

// For update
int updateBatch(List<?> entityList);

SqlAgent#close() should be commit/rollback before Connection#close()

Although SqlAgent#close() internally performs Connection#close(), depending on the DB (postgresql, etc.) if you close without commit / rollback, the resource remains locked.
You should commit / rollback explicitly before close.

Cited document
https://docs.oracle.com/javase/7/docs/api/java/sql/Connection.html#close()
It is strongly recommended that an application explicitly commits or rolls back an active transaction prior to calling the close method. If the close method is called and there is an active transaction, the results are implementation-defined.

Adding SqlFluent#param(String, Supplier<T>)

I am glad that you can implement SqlFluent#param's value in Lambda.

I would like to implement it as below.

sqlAgent.query("xxxx")
    .param("name", () -> condition ? "Tanaka" : "Yamada")
    .collect(Collectors.toList);

PreparedStatement is Leak.

When Query or Update is executed repeatedly for one Sql Agent, the Queue of the PreparedStatement in the LocalTransactionContext is not cleared and the memory is enlarged.

About Date & Time API support for MapResultSetConverter

In MapResultSetConverter, Date & Time API support is not available, for example, EntityResultSetConverter can convert to LocalDateTime or ZonedDateTime if the DB is of type timestamp, whereas in MapResultSetConveter it will bejava.sql.Timestamp.

For applications in Java 8 and later, since you think that there are many cases you want with ZonedDateTime, please consider.

Expand support for cipher algorism of SecretColumnFilter

SecretColumnFilter doesn't support any block-cipher modes which need initialization vector.
A Cipher object is initialized with SecretColumnFilter#initialize() method without any algorithm parameters.
I want to use CBC mode instead of ECB.

Stored function call fails in Oracle

The following stored function call fails in Oracle

{/*ret*/ = call func_xxx (/*param1*/}

The cause is that the bind parameter has been replaced
{?/*ret*/ = call func_xxx (?/*param1*/}
However, including a comment between "? =" Causes a grammar error.
{? = call func_xxx (?/*param1*/}
Can be executed if

I want to execute SqlUpdate#batch() only when parameters amount to provisioned (or specified by argument) number

		// The UPDATE statement will updates base on following query results.
		final SqlUpdate update = agent().update("Some update sql");

		// A query gets huge number of records.
		query.stream(new MapResultSetConverter(CaseFormat.CAMEL_CASE))
				.peek(r -> {
					// Query results relayed to UPDATE.
					update.paramMap(r).addBatch();

					//FIXME: I want to Execute 'batch()' every each 1000 parameters.
					if (update.context().batchCount() == 1000) {
						update.batch();
					}
				})
				.count();

		// for surplus results of above query
		update.batch();

For example:

		// specify frame size to 1000
		final SqlUpdate update = agent().update("Some update sql").batchFrame(1000);

		query.stream(new MapResultSetConverter(CaseFormat.CAMEL_CASE))
				.peek(r -> update.paramMap(r).addBatch().batch()) // this 'batch' will make sence only for each 1000 parameters
				.count();

		// remove frame and execute 'batch()'
		update.unframe().batch(); // or ".batchFrame(1)"

Another Solution:

		query.stream(new MapResultSetConverter(CaseFormat.CAMEL_CASE))
				.peek(r -> update.paramMap(r).addBatch().batchEvery(1000)) 
				.count();

		update.batch();

Incidentally: If I can use Stream#onClose

		query.stream(new MapResultSetConverter(CaseFormat.CAMEL_CASE))
				.onClose(() -> update.batch())
				.peek(r -> update.paramMap(r).addBatch().batchEvery(1000)) 
				.count();

		// update.batch(); // This is no longer needed.

An error occurs in `.queryWith("...").stream().map(r -> r).iterator()`

When generating a Stream from a query, creating an iterator from a Stream, and iterating, an error occurs at the last hasNext().

The following code will not cause an error,

sqlAgent
    .queryWith("...")
    .stream()
    .iterator()

But, the following code will cause an error.

sqlAgent
    .queryWith("...")
    .stream()
    .map(r -> r)
    .iterator()

When the column name of "OR" beginning is included in the where clause, it is executed with the column name excluding the character of "OR"

概要

where句に「OR」始まりのカラム名が含まれる場合、「OR」を除いたカラム名でSQLが実行される

再現SQL例

SELECT
    ORD_NO
FROM
    ORD_LIST
WHERE
    ORD_NO  =   1

バージョン

0.2.0

改修対象ソース

SqlContextImpl.java

必要と思われる改修

改修前:
protected static final Pattern WHERE_CLAUSE_PATTERN = Pattern
.compile("(?i)(WHERE(\s+(/\.\/|--.)+)*\s+)(AND|OR)");

改修後:
protected static final Pattern WHERE_CLAUSE_PATTERN = Pattern
.compile("(?i)(WHERE(\s+(/\.\/|--.)+)*\s+)(AND|OR)\s");

add dialect api

Since there are cases where processing contents and SQL to be constructed are changed depending on the type of Database, we want to add an API for handling the dialect of Database.

CallableStatement do not set fetchsize

When executing a procedure using MariaDB Connector / J, if FetchSize is specified for CallableStatement, a NullPointerException occurs with close () of CallableStatement.
This is a bug in MariaDB Connector / J.
To avoid this bug, please do not set FetchSize specification in SqlAgentFactory to CallableStatement because FetchSize specification is meaningless in CallableStatement.

I want to handling failure of pecimistic lock especially

I want to handling failure of pecimistic lock especially.

sqlConfig.getSqlAgentFactory().setSqlRetryCodeList(Arrays.asList("54", "30006"));
try {
    agent.update("example/insert_product").param("product_id", 1).retry(5, 20).count();
} catch (PecimistickLockException e) { // explicit exception
    ...
}

or

try {
    agent.update("example/insert_product").param("product_id", 1).retry(5, 20).count();
} catch (UroborosqlSQLException e) {
    if(agent.isRetryCode(e.getErrorCode())) { // some way to determine RetryCodeList
        ...
}

When the result is null, have you assigned 0 to Integer?

When the result is null, have you assigned 0 to Integer?
If the result is null if possible, please assign null to Integer.

I used SqlAgent.query().param().collect()

The way I am thinking fix, it is as follows.

PropertyMapperManager.class

Before

	if (Integer.class.equals(rawType) || int.class.equals(rawType)) {
		return rs.getInt(columnIndex);
	}

After

        if (Integer.class.equals(rawType)) {
            int result = rs.getInt(columnIndex);
            if (rs.wasNull()) {
                return null;
            }
            return result;
        }
        if (int.class.equals(rawType)) {
            return rs.getInt(columnIndex);
        }

I would like ResultSetConverter to convert CLOB, BLOB, and Array to a manageable type instead of getting it as an object

If you select columns that contain CLOBs or BLOBs with agent.query().collect() ,
you can get instances of java.sql.Clob or java.sql.Blob.
At the retrieval source, it is necessary to describe processing to acquire column values from Clob and Blob afterwards,
but since it becomes redundant, please go through ResultSetConverter until obtaining the column value.

The target column type is as follows

  • java.sql.Clob -> String
  • java.sql.NClob -> String
  • java.sql.Blob -> byte []
  • java.sql.Array -> (BaseType) []

Make the initial value of CaseFormat changeable

In SqlQuery#collect(), SqlQuery#find(), SqlQuery#findFirst(), the return value key is generated with CaseFormat#UPPER_SNAKE_CASE.
To generate a key in a format other than UPPER_SNAKE_CASE, you need to specify CaseFormat as a method argument.
However, since to specify CaseFormat whenever you call a method is redundant, I want to be able to specify the CaseFormat which is used as an initial value.

bag report : call transformContext before autoParameterBinder is accepted

related with #93
Because transformContext method is called before autoParameterBinder is accepted, the "IF" statement will be evaluated before it is added to the bind parameter.

// コンテキスト変換
transformContext(sqlContext); // before autoParameterBinder is accepted.
StopWatch watch = null;
try (PreparedStatement stmt = getPreparedStatement(sqlContext)) {
	// INパラメータ設定
	sqlContext.bindParams(stmt);
	if (LOG.isDebugEnabled()) {
		LOG.debug("Execute update SQL.");
		watch = new StopWatch();
		watch.start();
	}

Add SqlAgent#delete overload method

Currently, the SqlAgent#delete method is int delete (Object entity) and takes an entity class as an argument, but it seems that there may be an overloaded method which executes by passing only the primary key .

The signature you want to add is below.

<E> int delete(Class<? extends E> entityType, Object... keys)

Add method of extraction condition in SqlEntityQuery

Since SqlEntityQuery only supports equality of extraction conditions, why not add the following support?

method generated sql comment
paramNotEqual("col", "value") col != 'value' or col <> 'value' Do you support both operators?
paramIn("col", "val1", "val2") paramInList("col", List.of("val1", "val2") col in ('val1', 'val2') Is the method overloaded?
paramLike("col", "%val%") col like '%val%' Do you want '%' to autocomplete?
paramBetween("col", 1, 2) col between 1 and 2
paramIsNull("col") col is null
paramIsNotNull("col") col is not null
paramGreaterThan("col", 1) col > 1
paramLessThan("col", 1) col < 1
paramGreaterEqual("col", 1) col >= 1
paramLessEqual("col", 1) col <= 1
paramRaw("col = 1 or col = 2") (col = 1 or col = 2)
orderByAsc("col1", "col2") order by col1, col2 asc Do you support null first?
orderByDesc("col1", "col2") order by col1, cols desc
offset(10) offset 10
limit(10) limit 10

I want support for Statement#getGeneratedKeys()

For example, when inserting to a table with a column of auto increment type such as PostgreSQL, it gets an instance of PreparedStatement with Connection#prepareStatement(String, int) and it is generated by Statement#getGeneratedKeys() after issuing INSERT statement You can get the value, uroboroSQL does not have an API to get this, so I would like you to add it.

What you want to do is JDBC and it will be implemented as follows.

long key = -1L;
Statement statement = connection.createStatement();
statement.executeUpdate(YOUR_SQL_HERE, Statement.RETURN_GENERATED_KEYS);
ResultSet rs = statement.getGeneratedKeys();
if (rs != null && rs.next()) {
    key = rs.getLong(1);
}

I quoted from here.

https://stackoverflow.com/questions/4224228/preparedstatement-with-statement-return-generated-keys

CaseFormat should have lowercase SnakeCase.

It is a code like the following.

agent.query("owners-find")
    .param("lastName", lastName)
    .collect(CaseFormat.LowerSnakeCase);

In the case of SpringBoot, when returning JSON with the Controller, there are cases where lowercase letters of the map key are ideal.

Make it possible to switch SQL files to be loaded using Dialect

In order to make it easy to use in multi DB, add a mechanism to switch SQL referenced by Dialect.

example)

sql/
  sample/
    sample_query.sql

  oracle/
    sample/
      sample_query.sql

  postgresql/
    sample/
      sample_query.sql
  • In Oracle, use sql/oracle/sample/sample_query.sql
  • In Postgresql, use sql/postgresql/sample/sample_query.sql
  • Other(H2 etc.), use default, sql/sample/sample_query.sql

@Transient is also excluded from the result of SqlAgent#find.

Since I'm automatically assigning a primary key in PostgreSQL serial type, I added @Transient to exclude id column from INSERT statement at SqlAgent#insert.

INSERT will now work, but this time I encountered a bug that will be excluded from the result of SqlAgent#find, so I would like to ask you to fix it.

// Entity class
@Table(name = "hoge_tables")
public class HogeTable {
	@Transient
	@NotNull
	private Integer id;
...
}

// My logic
HogeTable hoge = sqlConfig.agent().find(HogeTable.class, 1);
System.out.println(hoge.getId()); // null

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.