Skip to content

Commit

Permalink
SqlEntityQuery#sum suports Optional type. (#316)
Browse files Browse the repository at this point in the history
* SqlEntityQuery#sum suports Optional type.

* isNumberTypeColumn move to MappingColumn
  • Loading branch information
HidekiSugimoto189 authored Nov 3, 2023
1 parent 775d572 commit 217d183
Show file tree
Hide file tree
Showing 6 changed files with 1,792 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -256,13 +256,7 @@ public long count(final String col) {
public <T> T sum(final String col) {
String camelColumnName = CaseFormat.CAMEL_CASE.convert(col);
MappingColumn mappingColumn = MappingUtils.getMappingColumn(context().getSchema(), entityType, camelColumnName);
Class<?> rawType = mappingColumn.getJavaType().getRawType();
if (!(short.class.equals(rawType) ||
int.class.equals(rawType) ||
long.class.equals(rawType) ||
float.class.equals(rawType) ||
double.class.equals(rawType) ||
Number.class.isAssignableFrom(mappingColumn.getJavaType().getRawType()))) {
if (!mappingColumn.isNumber()) {
throw new UroborosqlRuntimeException("Column is not of type Number. col=" + camelColumnName);
}
TableMetadata.Column column = tableMetadata.getColumn(camelColumnName);
Expand Down
84 changes: 71 additions & 13 deletions src/main/java/jp/co/future/uroborosql/mapping/MappingColumn.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
*/
package jp.co.future.uroborosql.mapping;

import java.time.temporal.Temporal;
import java.util.Optional;

import jp.co.future.uroborosql.enums.SqlKind;
import jp.co.future.uroborosql.mapping.annotations.GeneratedValue;
import jp.co.future.uroborosql.mapping.annotations.SequenceGenerator;
Expand All @@ -20,65 +23,65 @@
public interface MappingColumn {

/**
* エンティティから値を取得
* エンティティから値を取得.
*
* @param entity エンティティ
* @return 取得した値
*/
Object getValue(Object entity);

/**
* エンティティに値をセット
* エンティティに値をセット.
*
* @param entity エンティティ
* @param value 値
*/
void setValue(Object entity, Object value);

/**
* カラム名取得
* カラム名取得.
*
* @return カラム名
*/
String getName();

/**
* キャメルケースカラム名取得
* キャメルケースカラム名取得.
*
* @return キャメルケースカラム名
*/
String getCamelName();

/**
* {@link JavaType}取得
* {@link JavaType}取得.
*
* @return {@link JavaType}
*/
JavaType getJavaType();

/**
* IDアノテーションが付与されているかどうか
* IDアノテーションが付与されているかどうか.
*
* @return IDアノテーションが付与されている場合<code>true</code>
*/
boolean isId();

/**
* {@link GeneratedValue}の取得
* {@link GeneratedValue}の取得.
*
* @return {@link GeneratedValue}
*/
GeneratedValue getGeneratedValue();

/**
* {@link SequenceGenerator}の取得
* {@link SequenceGenerator}の取得.
*
* @return {@link SequenceGenerator}
*/
SequenceGenerator getSequenceGenerator();

/**
* 修飾済みのシーケンス名の取得
* 修飾済みのシーケンス名の取得.
*
* @return {@link SequenceGenerator} をもとに修飾したシーケンス名
*/
Expand All @@ -99,29 +102,84 @@ default String getQualifiedSequenceName() {
}

/**
* {@link Transient}の取得
* {@link Transient}の取得.
*
* @return {@link Transient}
*/
Transient getTransient();

/**
* 指定したSQL種別でtransientかどうかを判断する
* 指定したSQL種別でtransientかどうか.
*
* @param sqlKind SQL種別
* @return 指定したSQL種別でtransientの場合<code>true</code>
*/
boolean isTransient(SqlKind sqlKind);

/**
* バージョン情報カラムかどうか
* バージョン情報カラムかどうか.
*
* @return バージョンカラムの場合は<code>true</code>
*/
boolean isVersion();

/**
* {@link Version} の取得
* Optional型のカラムかどうか.
*
* @return Optional型のカラムの場合は<code>true</code>
*/
default boolean isOptional() {
return Optional.class.equals(getJavaType().getRawType());
}

/**
* 文字、または文字列型のカラムかどうか.
*
* @return 文字、または文字列型のカラムの場合は<code>true</code>
*/
default boolean isString() {
Class<?> rawType = isOptional() ? getJavaType().getParam(0).getRawType() : getJavaType().getRawType();
return String.class.equals(rawType) ||
char.class.equals(rawType) ||
Character.class.equals(rawType);
}

/**
* 数値型のカラムかどうか.
*
* @return 数値型のカラムの場合は<code>true</code>
*/
default boolean isNumber() {
Class<?> rawType = isOptional() ? getJavaType().getParam(0).getRawType() : getJavaType().getRawType();
return short.class.equals(rawType) ||
int.class.equals(rawType) ||
long.class.equals(rawType) ||
float.class.equals(rawType) ||
double.class.equals(rawType) ||
Number.class.isAssignableFrom(rawType);
}

/**
* 配列型のカラムかどうか.
*
* @return 配列型のカラムの場合は<code>true</code>
*/
default boolean isArray() {
return getJavaType().getRawType().isArray();
}

/**
* 時間的オブジェクト型のカラムかどうか.
*
* @return 時間的オブジェクト型のカラムの場合は<code>true</code>
*/
default boolean isTemporal() {
Class<?> rawType = isOptional() ? getJavaType().getParam(0).getRawType() : getJavaType().getRawType();
return Temporal.class.isAssignableFrom(rawType);
}

/**
* {@link Version} の取得.
*
* @return {@link Version}
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import static org.junit.Assert.fail;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
Expand Down Expand Up @@ -331,6 +332,112 @@ public void testQuery4() throws Exception {
}
}

@Test
public void testQuery5() throws Exception {

try (SqlAgent agent = config.agent()) {
agent.required(() -> {
TestEntity4 test1 = new TestEntity4(1L, "name1", new BigDecimal("20"),
LocalDate.of(1990, Month.APRIL, 1));
agent.insert(test1);
TestEntity4 test2 = new TestEntity4(2L, "name2", new BigDecimal("21"),
LocalDate.of(1990, Month.MAY, 1));
agent.insert(test2);
TestEntity4 test3 = new TestEntity4(3L, "name3", new BigDecimal("22"),
LocalDate.of(1990, Month.MAY, 1));
agent.insert(test3);
TestEntity4 test4 = new TestEntity4(4L, "name4", new BigDecimal("23"), null);
agent.insert(test4);

long count1 = agent.query(TestEntity4.class).count();
assertThat(count1, is(4L));
long count2 = agent.query(TestEntity4.class).count(TestEntity4.Names.Birthday);
assertThat(count2, is(3L));

BigDecimal sum = agent.query(TestEntity4.class).sum(TestEntity4.Names.Age);
assertThat(sum, is(new BigDecimal("86")));

BigDecimal min = agent.query(TestEntity4.class).min(TestEntity4.Names.Age);
assertThat(min, is(new BigDecimal("20")));

String minName = agent.query(TestEntity4.class).min(TestEntity4.Names.Name);
assertThat(minName, is("name1"));

long max = agent.query(TestEntity4.class).max(TestEntity4.Names.Id);
assertThat(max, is(4L));

LocalDate maxDate = agent.query(TestEntity4.class).max(TestEntity4.Names.Birthday);
assertThat(maxDate, is(LocalDate.of(1990, Month.MAY, 1)));
});
}
}

@Test
public void testQuery6() throws Exception {

try (SqlAgent agent = config.agent()) {
agent.required(() -> {
TestEntity5 test1 = new TestEntity5(1L, "name1", Optional.ofNullable(new BigDecimal("20")),
LocalDate.of(1990, Month.APRIL, 1));
agent.insert(test1);
TestEntity5 test2 = new TestEntity5(2L, "name2", Optional.ofNullable(new BigDecimal("21")),
LocalDate.of(1990, Month.MAY, 1));
agent.insert(test2);
TestEntity5 test3 = new TestEntity5(3L, "name3", Optional.ofNullable(new BigDecimal("22")),
LocalDate.of(1990, Month.MAY, 1));
agent.insert(test3);
TestEntity5 test4 = new TestEntity5(4L, "name4", Optional.empty(), null);
agent.insert(test4);

long count1 = agent.query(TestEntity5.class).count();
assertThat(count1, is(4L));
long count2 = agent.query(TestEntity5.class).count(TestEntity5.Names.Birthday);
assertThat(count2, is(3L));

Optional<BigDecimal> sum = agent.query(TestEntity5.class).sum(TestEntity5.Names.Age);
assertThat(sum.orElseThrow(IllegalStateException::new), is(new BigDecimal("63")));

Optional<BigDecimal> min = agent.query(TestEntity5.class).min(TestEntity5.Names.Age);
assertThat(min.orElseThrow(IllegalStateException::new), is(new BigDecimal("20")));

String minName = agent.query(TestEntity5.class).min(TestEntity5.Names.Name);
assertThat(minName, is("name1"));

long max = agent.query(TestEntity5.class).max(TestEntity5.Names.Id);
assertThat(max, is(4L));

LocalDate maxDate = agent.query(TestEntity5.class).max(TestEntity5.Names.Birthday);
assertThat(maxDate, is(LocalDate.of(1990, Month.MAY, 1)));

});
}
}

@Test
public void testQuery7() throws Exception {

try (SqlAgent agent = config.agent()) {
agent.required(() -> {
TestEntity5 test1 = new TestEntity5(1L, "name1", Optional.empty(),
LocalDate.of(1990, Month.APRIL, 1));
agent.insert(test1);
TestEntity5 test2 = new TestEntity5(2L, "name2", Optional.empty(),
LocalDate.of(1990, Month.MAY, 1));
agent.insert(test2);

Optional<BigDecimal> sum = agent.query(TestEntity5.class).sum(TestEntity5.Names.Age);
assertThat(sum.isPresent(), is(false));

Optional<BigDecimal> min = agent.query(TestEntity5.class).min(TestEntity5.Names.Age);
assertThat(min.isPresent(), is(false));

Optional<BigDecimal> max = agent.query(TestEntity5.class).max(TestEntity5.Names.Age);
assertThat(max.isPresent(), is(false));

});
}
}

@Test(expected = UroborosqlRuntimeException.class)
public void testQueryCountUnmatchColumn() throws Exception {
try (SqlAgent agent = config.agent()) {
Expand Down Expand Up @@ -399,9 +506,7 @@ public void testQueryWithCondition() throws Exception {
TestEntity test3 = new TestEntity(3L, "name3", 20, LocalDate.of(1990, Month.JUNE, 1), Optional.empty());
agent.insert(test3);

// Equal
List<TestEntity> list = null;
list = agent.query(TestEntity.class).equal("id", 2).collect();
List<TestEntity> list = agent.query(TestEntity.class).equal("id", 2).collect();
assertThat(list.size(), is(1));
assertThat(list.get(0), is(test2));

Expand Down Expand Up @@ -700,9 +805,8 @@ public void testQueryWithBetweenColumns() throws Exception {
"name3");
agent.insert(test3);

List<TestHistoryEntity> list = null;
// Between
list = agent.query(TestHistoryEntity.class)
List<TestHistoryEntity> list = agent.query(TestHistoryEntity.class)
.betweenColumns(LocalDate.of(1990, Month.APRIL, 15), "start_at", "finish_at")
.asc("id")
.collect();
Expand Down Expand Up @@ -738,9 +842,8 @@ public void testQueryWithNotBetweenColumns() throws Exception {
"name3");
agent.insert(test3);

List<TestHistoryEntity> list = null;
// Between
list = agent.query(TestHistoryEntity.class)
List<TestHistoryEntity> list = agent.query(TestHistoryEntity.class)
.notBetweenColumns(LocalDate.of(1990, Month.APRIL, 15), "start_at", "finish_at")
.asc("id")
.collect(); // 4/15 < start_at or 4/15 > finish_at
Expand Down
Loading

0 comments on commit 217d183

Please sign in to comment.