Skip to content

Commit

Permalink
Merge branch '4.10.x' into jdbc-clientinfo
Browse files Browse the repository at this point in the history
  • Loading branch information
radovanradic committed Nov 29, 2024
2 parents 7566f76 + 86a54bb commit a5d01a8
Show file tree
Hide file tree
Showing 14 changed files with 116 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -202,13 +202,15 @@ class PageSpec extends Specification {
def pageable = Pageable.from(0, 10)
Page<Person> page = personRepository.findByNameLike("A%", pageable)
Page<Person> page2 = crudRepository.findPeople("A%", pageable)
Page<Person> page3 = crudRepository.findPeopleNative("A%", pageable)
Slice<Person> slice = personRepository.queryByNameLike("A%", pageable)

then:"The page is correct"
page.offset == 0
page.pageNumber == 0
page.totalSize == 50
page2.totalSize == page.totalSize
page3.totalSize == page.totalSize
slice.offset == 0
slice.pageNumber == 0
slice.size == 10
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ public BookRepository(AuthorRepository authorRepository) {
super(authorRepository);
}

@Override
public abstract Book save(Book book);

/**
* @deprecated Order by 'author.name' case without a join. Hibernate will do the cross join if the association property is accessed by the property path without join.
*/
Expand Down Expand Up @@ -71,7 +74,7 @@ public BookRepository(AuthorRepository authorRepository) {
@Query(value = "select count(*) from book b where b.title like :title and b.total_pages > :pages", nativeQuery = true)
abstract int countNativeByTitleWithPagesGreaterThan(String title, int pages);

@Query(value = "select * from book where (CASE WHEN CAST(:arg0 AS VARCHAR) is not null THEN title = :arg0 ELSE true END)", nativeQuery = true)
@Query(value = "select * from book where (CASE WHEN CAST(:arg0 AS VARCHAR) is not null THEN title = :arg0 ELSE true END) FOR UPDATE", nativeQuery = true)
public abstract List<Book> listNativeBooksNullableSearch(@Nullable String arg0);

@Query(value = "select * from book where (CASE WHEN exists ( select (:arg0) ) THEN title IN (:arg0) ELSE true END)", nativeQuery = true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ public interface PersonCrudRepository extends JpaRepository<Person, Long>, Perso
@Transactional
Page<Person> findPeople(String n, Pageable pageable);

@Query(value = "SELECT * FROM person WHERE name LIKE :n",
countQuery = "SELECT COUNT(*) FROM person WHERE name LIKE :n",
nativeQuery = true)
@Transactional
Page<Person> findPeopleNative(String n, Pageable pageable);

@Query("from Person p where p.name = :n")
@Transactional
Person queryByName(String n);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

import io.micronaut.context.BeanLocator;
import io.micronaut.context.annotation.Context;
import io.micronaut.context.env.Environment;
import io.micronaut.context.env.PropertyPlaceholderResolver;
import io.micronaut.context.exceptions.ConfigurationException;
import io.micronaut.context.exceptions.NoSuchBeanException;
import io.micronaut.core.annotation.Internal;
Expand Down Expand Up @@ -61,16 +63,21 @@ public class SchemaGenerator {

private final List<DataJdbcConfiguration> configurations;
private final JdbcSchemaHandler schemaHandler;
private final PropertyPlaceholderResolver propertyPlaceholderResolver;

/**
* Constructors a schema generator for the given configurations.
*
* @param configurations The configurations
* @param schemaHandler The schema handler
* @param environment The environment
*/
public SchemaGenerator(List<DataJdbcConfiguration> configurations, JdbcSchemaHandler schemaHandler) {
public SchemaGenerator(List<DataJdbcConfiguration> configurations,
JdbcSchemaHandler schemaHandler,
Environment environment) {
this.configurations = configurations == null ? Collections.emptyList() : configurations;
this.schemaHandler = schemaHandler;
this.propertyPlaceholderResolver = environment.getPlaceholderResolver();
}

/**
Expand Down Expand Up @@ -115,14 +122,14 @@ public void createSchema(BeanLocator beanLocator) {
for (String schemaName : configuration.getSchemaGenerateNames()) {
schemaHandler.createSchema(connection, dialect, schemaName);
schemaHandler.useSchema(connection, dialect, schemaName);
generate(connection, configuration, entities);
generate(connection, configuration, propertyPlaceholderResolver, entities);
}
} else {
if (configuration.getSchemaGenerateName() != null) {
schemaHandler.createSchema(connection, dialect, configuration.getSchemaGenerateName());
schemaHandler.useSchema(connection, dialect, configuration.getSchemaGenerateName());
}
generate(connection, configuration, entities);
generate(connection, configuration, propertyPlaceholderResolver, entities);
}
} catch (SQLException e) {
throw new DataAccessException("Unable to create database schema: " + e.getMessage(), e);
Expand All @@ -136,14 +143,15 @@ public void createSchema(BeanLocator beanLocator) {

private static void generate(Connection connection,
DataJdbcConfiguration configuration,
PropertyPlaceholderResolver propertyPlaceholderResolver,
PersistentEntity[] entities) throws SQLException {
Dialect dialect = configuration.getDialect();
SqlQueryBuilder2 builder = new SqlQueryBuilder2(dialect);
if (dialect.allowBatch() && configuration.isBatchGenerate()) {
switch (configuration.getSchemaGenerate()) {
case CREATE_DROP:
try {
String sql = builder.buildBatchDropTableStatement(entities);
String sql = resolveSql(propertyPlaceholderResolver, builder.buildBatchDropTableStatement(entities));
if (DataSettings.QUERY_LOG.isDebugEnabled()) {
DataSettings.QUERY_LOG.debug("Dropping Tables: \n{}", sql);
}
Expand All @@ -156,7 +164,7 @@ private static void generate(Connection connection,
}
}
case CREATE:
String sql = builder.buildBatchCreateTableStatement(entities);
String sql = resolveSql(propertyPlaceholderResolver, builder.buildBatchCreateTableStatement(entities));
if (DataSettings.QUERY_LOG.isDebugEnabled()) {
DataSettings.QUERY_LOG.debug("Creating Tables: \n{}", sql);
}
Expand All @@ -174,6 +182,7 @@ private static void generate(Connection connection,
try {
String[] statements = builder.buildDropTableStatements(entity);
for (String sql : statements) {
sql = resolveSql(propertyPlaceholderResolver, sql);
if (DataSettings.QUERY_LOG.isDebugEnabled()) {
DataSettings.QUERY_LOG.debug("Dropping Table: \n{}", sql);
}
Expand All @@ -192,6 +201,7 @@ private static void generate(Connection connection,

String[] sql = builder.buildCreateTableStatements(entity);
for (String stmt : sql) {
stmt = resolveSql(propertyPlaceholderResolver, stmt);
if (DataSettings.QUERY_LOG.isDebugEnabled()) {
DataSettings.QUERY_LOG.debug("Executing CREATE statement: \n{}", stmt);
}
Expand All @@ -215,4 +225,18 @@ private static void generate(Connection connection,
}
}
}

/**
* Resolves property placeholder values if there are any.
*
* @param propertyPlaceholderResolver The property placeholder resolver
* @param sql The SQL to resolve placeholder properties if there are any
* @return The resulting SQL with resolved properties if there were any
*/
private static String resolveSql(PropertyPlaceholderResolver propertyPlaceholderResolver, String sql) {
if (sql.contains(propertyPlaceholderResolver.getPrefix())) {
return propertyPlaceholderResolver.resolveRequiredPlaceholders(sql);
}
return sql;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ trait H2TestPropertyProvider implements TestPropertyProvider {
}

Map<String, String> getProperties() {
return shouldAddDefaultDbProperties()? getH2DataSourceProperties("default") : [:]
return shouldAddDefaultDbProperties() ? getH2DataSourceProperties("default") : [:]
}

Map<String, String> getH2DataSourceProperties(String dataSourceName) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package io.micronaut.data.jdbc.h2.identity

import io.micronaut.context.annotation.Property
import io.micronaut.data.jdbc.h2.H2DBProperties
import io.micronaut.test.extensions.spock.annotation.MicronautTest
import jakarta.inject.Inject
import spock.lang.Specification

@H2DBProperties
@Property(name = "datasources.default.packages", value = "io.micronaut.data.jdbc.h2.identity")
@Property(name = "datasources.default.batch-generate", value = "true")
@MicronautTest
class SameIdentityRepositorySpec extends Specification {
@Inject
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,8 @@ private void processMethodInfo(MethodMatchContext methodMatchContext, MethodMatc
}

builder.member(AnnotationMetadata.VALUE_MEMBER, query);
builder.member(DataMethodQuery.META_MEMBER_NATIVE, element.booleanValue(Query.class,
DataMethodQuery.META_MEMBER_NATIVE).orElse(false));

addQueryDefinition(methodMatchContext,
builder,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
public class RawQueryMethodMatcher implements MethodMatcher {

private static final Pattern UPDATE_PATTERN = Pattern.compile(".*\\bupdate\\b.*");
private static final Pattern FOR_UPDATE_PATTERN = Pattern.compile("for\\s+update");
private static final Pattern DELETE_PATTERN = Pattern.compile(".*\\bdelete\\b.*");
private static final Pattern INSERT_PATTERN = Pattern.compile(".*\\binsert\\b.*");
private static final Pattern RETURNING_PATTERN = Pattern.compile(".*\\breturning\\b.*");
Expand Down Expand Up @@ -185,7 +186,9 @@ private DataMethod.OperationType findOperationType(String methodName, String que
if (DeleteMethodMatcher.METHOD_PATTERN.matcher(methodName.toLowerCase(Locale.ENGLISH)).matches()) {
return DataMethod.OperationType.DELETE;
}
return DataMethod.OperationType.UPDATE;
if (!FOR_UPDATE_PATTERN.matcher(query).find()) {
return DataMethod.OperationType.UPDATE;
}
} else if (INSERT_PATTERN.matcher(query).find()) {
if (RETURNING_PATTERN.matcher(query).find()) {
return DataMethod.OperationType.INSERT_RETURNING;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2088,7 +2088,7 @@ import io.micronaut.data.jdbc.annotation.JdbcRepository;
import io.micronaut.data.model.query.builder.sql.Dialect;
import io.micronaut.data.tck.entities.Person;
@JdbcRepository(dialect= Dialect.MYSQL)
@JdbcRepository(dialect = Dialect.MYSQL)
@io.micronaut.context.annotation.Executable
interface PersonRepository extends CrudRepository<Person, Long> {
Expand All @@ -2098,14 +2098,25 @@ interface PersonRepository extends CrudRepository<Person, Long> {
\""")
$type customSelect(Long id);
@Query(\"""
SELECT * FROM person WHERE id = :id FOR
UPDATE
\""")
$type selectForUpdate(Long id);
}
""")
def method = repository.findPossibleMethods("customSelect").findFirst().get()
def selectQuery = getQuery(method)
def customSelectMethod = repository.findPossibleMethods("customSelect").findFirst().get()
def customSelectQuery = getQuery(customSelectMethod)
def selectForUpdateMethod = repository.findPossibleMethods("selectForUpdate").findFirst().get()
def selectForUpdateQuery = getQuery(selectForUpdateMethod)

expect:
selectQuery.replace('\n', ' ') == "WITH ids AS (SELECT id FROM person) SELECT * FROM person "
method.classValue(DataMethod, "interceptor").get() == interceptor
customSelectQuery.replace('\n', ' ') == "WITH ids AS (SELECT id FROM person) SELECT * FROM person "
customSelectMethod.classValue(DataMethod, "interceptor").get() == interceptor

selectForUpdateQuery.replace('\n', ' ') == "SELECT * FROM person WHERE id = :id FOR UPDATE "
selectForUpdateMethod.classValue(DataMethod, "interceptor").get() == interceptor

where:
type | interceptor
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package example;

import io.micronaut.data.annotation.GeneratedValue;
import io.micronaut.data.annotation.Id;
import io.micronaut.data.annotation.MappedEntity;

@MappedEntity("${entity.prefix}entity")
public record CustomEntity(
@Id
@GeneratedValue
Long id,
String name) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package example;

import io.micronaut.data.jdbc.annotation.JdbcRepository;
import io.micronaut.data.model.query.builder.sql.Dialect;
import io.micronaut.data.repository.CrudRepository;

@JdbcRepository(dialect = Dialect.H2)
public interface CustomEntityRepository extends CrudRepository<CustomEntity, Long> {
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ datasources:
password: ''
schema-generate: CREATE_DROP
dialect: H2
entity:
prefix: demo_
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package example;

import io.micronaut.test.extensions.junit5.annotation.MicronautTest;
import jakarta.inject.Inject;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

@MicronautTest
class CustomEntityRepositorySpec {

@Inject
CustomEntityRepository repository;

@Test
void testSaveAndFind() {
CustomEntity entity = repository.save(new CustomEntity(null, "Entity1"));
CustomEntity found = repository.findById(entity.id()).orElse(null);
Assertions.assertNotNull(found);
Assertions.assertEquals(entity.name(), found.name());
Assertions.assertEquals(1, repository.count());
Assertions.assertFalse(repository.findAll().isEmpty());
repository.deleteAll();
}
}
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[versions]
micronaut = "4.7.5"
micronaut = "4.7.6"
micronaut-platform = "4.6.3"
micronaut-docs = "2.0.0"
micronaut-gradle-plugin = "4.4.4"
Expand Down

0 comments on commit a5d01a8

Please sign in to comment.