Skip to content
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
2a15259
Fix schema generator and query execution when entities have dynamic/c…
radovanradic Apr 28, 2025
602af16
fix(deps): update dependency io.micronaut:micronaut-core-bom to v4.8.…
renovate[bot] Apr 28, 2025
437c889
Fix for EmbeddedId regression in 4.0
radovanradic Apr 28, 2025
8f17328
Merge branch '4.12.x' into embeddedid-colname
radovanradic Apr 28, 2025
c688c3e
chore(deps): update softprops/action-gh-release action to v2.2.2 (#3404)
renovate[bot] Apr 28, 2025
46fc27f
fix(deps): update dependency org.springframework.boot:spring-boot-gra…
renovate[bot] Apr 29, 2025
2f35eac
fix(deps): update spring data (#3406)
renovate[bot] Apr 29, 2025
e647ccf
Don't use address prefix for embedded field
radovanradic Apr 29, 2025
a734546
Merge branch '4.12.x' into embeddedid-colname
radovanradic Apr 29, 2025
e1c9833
Add one more example for embedded and composite id
radovanradic Apr 29, 2025
64b1157
Disable gradle caching temporary
radovanradic Apr 29, 2025
70f769f
Revert gradle caching
radovanradic Apr 29, 2025
c3c5883
Add more tests for repo
radovanradic Apr 29, 2025
e9d666b
Remove trigger builds from workflow
radovanradic Apr 29, 2025
03cfb51
Fix issue with CursoredPageable and @EmbeddedId (#3411)
radovanradic Apr 30, 2025
4ad478c
Improve the Oracle client info feature. (#3405)
mikehearn Apr 30, 2025
70b14f5
fix(deps): update dependency io.micronaut:micronaut-core-bom to v4.8.…
renovate[bot] May 1, 2025
c5e4646
fix(deps): update dependency io.micronaut.coherence:micronaut-coheren…
renovate[bot] May 2, 2025
22695c2
[skip ci] Release v4.12.1
micronaut-build May 5, 2025
c771071
chore: Bump version to 4.12.2-SNAPSHOT
micronaut-build May 5, 2025
4886465
Merge branch '4.12.x' into embeddedid-colname
radovanradic May 5, 2025
4445e91
Merge remote-tracking branch 'origin/5.0.x' into embeddedid-colname
radovanradic Nov 5, 2025
8d8823a
Merge remote-tracking branch 'origin/5.0.x' into embeddedid-colname
radovanradic Nov 7, 2025
65e10b6
Fix tests
radovanradic Nov 7, 2025
f58c848
Revert formatting changes.
radovanradic Nov 7, 2025
70cc61f
Example for EmbeddedId naming strategy change
radovanradic Nov 9, 2025
55d5da7
Keep backward compatible EmbeddedId naming
radovanradic Nov 9, 2025
884b521
Add breaking changes documentation about composite id naming (@Embedd…
radovanradic Nov 9, 2025
c9ef288
Updated breaking changes documentation and tests.
radovanradic Nov 9, 2025
0d08c3a
Remove duplicated test example
radovanradic Nov 9, 2025
feabe3c
Merge remote-tracking branch 'origin/5.0.x' into embeddedid-colname
radovanradic Nov 10, 2025
a62cf87
Merge remote-tracking branch 'origin/5.0.x' into embeddedid-colname
radovanradic Nov 12, 2025
7ec83db
Merge remote-tracking branch 'origin/5.0.x' into embeddedid-colname
radovanradic Nov 28, 2025
a8137b0
Merge remote-tracking branch 'origin/5.0.x' into embeddedid-colname
radovanradic Dec 22, 2025
3883bed
Fix jakarta data test in data-processor
radovanradic Dec 22, 2025
db6cbba
Merge remote-tracking branch 'origin/5.0.x' into embeddedid-colname
radovanradic Dec 30, 2025
60c7ba3
Merge remote-tracking branch 'origin/5.0.x' into embeddedid-colname
radovanradic Jan 12, 2026
4301cc6
Merge remote-tracking branch 'origin/5.0.x' into embeddedid-colname
radovanradic Jan 16, 2026
71ac69c
Merge remote-tracking branch 'origin/5.0.x' into embeddedid-colname
radovanradic Jan 21, 2026
5553bb0
Merge remote-tracking branch 'origin/5.0.x' into embeddedid-colname
radovanradic Jan 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,6 @@ jobs:
- name: Upload assets
# Upload the artifacts to the existing release. Note that the SLSA provenance will
# attest to each artifact file and not the aggregated ZIP file.
uses: softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda # v2.2.1
uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2
with:
files: artifacts.zip
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ default Map<String, String> getProperties() {
return Map.of(
"jpa.default.properties.hibernate.hbm2ddl.auto", "create-drop",
"jpa.default.reactive", "true",
"jpa.default.properties.hibernate.connection.db-type", "mysql"
"jpa.default.properties.hibernate.connection.db-type", "mysql",
"test-resources.containers.mysql.image-name", "mysql:8.4.5"
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ class H2EmbeddedSpec extends Specification {
restaurant.address.street == 'Smith St.'
restaurant.address.zipCode == '1234'

when:"Find restaurant by street name"
restaurant = restaurantRepository.findByAddressStreet("Smith St.").orElse(null)
then:"Found restaurant"
restaurant
restaurant.name == "Joe's Cafe"

when:"Max by embedded property"
def maxStreet = restaurantRepository.getMaxAddressStreetByName("Fred's Cafe")
def minStreet = restaurantRepository.getMinAddressStreetByName("Fred's Cafe")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class EmbeddedAssociationJoinSpec extends Specification implements H2TestPropert
`id` bigint primary key not null,
`value` text,
`example` text,
`part_text` text);
`text` text);
""").execute()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class CustomEmbeddedNameMapping extends Specification implements H2TestPropertyP
def statements = encoder.buildCreateTableStatements(getRuntimePersistentEntity(MyBook))

then:
statements.join("\n") == 'CREATE TABLE "MyBook" ("id" VARCHAR(255) NOT NULL,"authorFirstName" VARCHAR(255) NOT NULL,"authorLastName" VARCHAR(255) NOT NULL,"authorDetailsIncludedNumberAge" INT NOT NULL, PRIMARY KEY("id"));'
statements.join("\n") == 'CREATE TABLE "MyBook" ("id" VARCHAR(255) NOT NULL,"firstName" VARCHAR(255) NOT NULL,"lastName" VARCHAR(255) NOT NULL,"numberAge" INT NOT NULL, PRIMARY KEY("id"));'
}

void "test build insert"() {
Expand All @@ -79,7 +79,7 @@ class CustomEmbeddedNameMapping extends Specification implements H2TestPropertyP
def res = encoder.buildInsert(AnnotationMetadata.EMPTY_METADATA, getRuntimePersistentEntity(MyBook))

then:
res.query == 'INSERT INTO "MyBook" ("authorFirstName","authorLastName","authorDetailsIncludedNumberAge","id") VALUES (?,?,?,?)'
res.query == 'INSERT INTO "MyBook" ("firstName","lastName","numberAge","id") VALUES (?,?,?,?)'
}

void "test update"() {
Expand All @@ -92,7 +92,7 @@ class CustomEmbeddedNameMapping extends Specification implements H2TestPropertyP
)

then:
res.query == 'UPDATE "MyBook" SET "id"=?,"authorFirstName"=?,"authorLastName"=?,"authorDetailsIncludedNumberAge"=? WHERE ("id" = ?)'
res.query == 'UPDATE "MyBook" SET "id"=?,"firstName"=?,"lastName"=?,"numberAge"=? WHERE ("id" = ?)'
res.parameters == [
'1':'id',
'2':'author.firstName',
Expand All @@ -107,7 +107,7 @@ class CustomEmbeddedNameMapping extends Specification implements H2TestPropertyP
QueryBuilder encoder = new SqlQueryBuilder()
def q = encoder.buildQuery(AnnotationMetadata.EMPTY_METADATA, QueryModel.from(getRuntimePersistentEntity(MyBook)).idEq(new QueryParameter("xyz")))
then:
q.query == 'SELECT my_book_."id",my_book_."authorFirstName",my_book_."authorLastName",my_book_."authorDetailsIncludedNumberAge" FROM "MyBook" my_book_ WHERE (my_book_."id" = ?)'
q.query == 'SELECT my_book_."id",my_book_."firstName",my_book_."lastName",my_book_."numberAge" FROM "MyBook" my_book_ WHERE (my_book_."id" = ?)'
}

@Shared
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,13 @@

import io.micronaut.data.jdbc.annotation.JdbcRepository;
import io.micronaut.data.model.query.builder.sql.Dialect;
import io.micronaut.data.tck.entities.Restaurant;
import io.micronaut.data.tck.repositories.RestaurantRepository;

import java.util.Optional;

@JdbcRepository(dialect = Dialect.H2)
public interface H2RestaurantRepository extends RestaurantRepository {

Optional<Restaurant> findByAddressStreet(String street);
}
1 change: 1 addition & 0 deletions data-jdbc/src/test/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ test-resources:
startup-timeout: 300s
mysql:
startup-timeout: 300s
image-name: mysql:8.4.5
oracle:
startup-timeout: 600s
db-name: test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,11 +148,13 @@ default String mappedAssociatedName(@NonNull String associatedName) {
foreignAssociation = association;
}
final String originalAssocName = association.getName();
String assocName = association.getKind() == Relation.Kind.EMBEDDED ? association.getAnnotationMetadata().stringValue(MappedProperty.class).orElse(originalAssocName) : originalAssocName;
if (!sb.isEmpty()) {
sb.append(mappedAssociatedName(assocName));
} else {
sb.append(assocName);
String assocName = association.getKind() == Relation.Kind.EMBEDDED ? association.getAnnotationMetadata().stringValue(MappedProperty.class).orElse(StringUtils.EMPTY_STRING) : originalAssocName;
if (StringUtils.isNotEmpty(assocName)) {
if (!sb.isEmpty()) {
sb.append(mappedAssociatedName(assocName));
} else {
sb.append(assocName);
}
}
}
if (foreignAssociation != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,18 @@ protected String getTableAsKeyword() {
* @return The quoted name
*/
protected String quote(String persistedName) {
return quote(persistedName, false);
}

/**
* Quote a persisted name (schema, table or column name) for the dialect.
*
* @param persistedName The persisted name.
* @param supportsDynamicValues Whether persisted name supports dynamic values. Schema and table can have
* dynamic value (like ${config.entry}) and columns can't.
* @return The quoted name
*/
protected String quote(String persistedName, boolean supportsDynamicValues) {
return "\"" + persistedName + "\"";
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,18 @@ protected String getTableAsKeyword() {
* @return The quoted name
*/
protected String quote(String persistedName) {
return quote(persistedName, false);
}

/**
* Quote a persisted name (schema, table or column name) for the dialect.
*
* @param persistedName The persisted name.
* @param supportsDynamicValues Whether persisted name supports dynamic values. Schema and table can have
* dynamic value (like ${config.entry}) and columns can't.
* @return The quoted name
*/
protected String quote(String persistedName, boolean supportsDynamicValues) {
return "\"" + persistedName + "\"";
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ String[] buildDropTableStatements(@NonNull PersistentEntity entity) {
.orElseGet(() ->
getMappedName(namingStrategy, association)
);
dropStatements.add("DROP TABLE " + (escape ? quote(joinTableName) : joinTableName) + ";");
dropStatements.add("DROP TABLE " + (escape ? quote(joinTableName, true) : joinTableName) + ";");
}

dropStatements.add(sql);
Expand Down Expand Up @@ -298,12 +298,12 @@ String buildJoinTableInsert(@NonNull PersistentEntity entity, @NonNull Associati
.orElseGet(() ->
getMappedName(namingStrategy, association)
);
joinTableName = quote(joinTableName);
joinTableName = quote(joinTableName, true);
String joinTableSchema = annotationMetadata
.stringValue(ANN_JOIN_TABLE, SqlMembers.SCHEMA)
.orElse(getSchemaName(entity));
if (StringUtils.isNotEmpty(joinTableSchema)) {
joinTableSchema = quote(joinTableSchema);
joinTableSchema = quote(joinTableSchema, true);
joinTableName = joinTableSchema + DOT + joinTableName;
}
List<String> leftJoinColumns = resolveJoinTableJoinColumns(annotationMetadata, true, entity, namingStrategy);
Expand Down Expand Up @@ -353,7 +353,7 @@ String[] buildCreateTableStatements(@NonNull PersistentEntity entity) {
String schema = getSchemaName(entity);
if (StringUtils.isNotEmpty(schema)) {
if (escape) {
schema = quote(schema);
schema = quote(schema, true);
}
createStatements.add("CREATE SCHEMA " + schema + ";");
}
Expand All @@ -376,12 +376,12 @@ String[] buildCreateTableStatements(@NonNull PersistentEntity entity) {
getMappedName(namingStrategy, association)
);
if (escape) {
joinTableName = quote(joinTableName);
joinTableName = quote(joinTableName, true);
}
String joinTableSchema = annotationMetadata.stringValue(ANN_JOIN_TABLE, SqlMembers.SCHEMA).orElse(null);
if (StringUtils.isNotEmpty(joinTableSchema)) {
if (escape) {
joinTableSchema = quote(joinTableSchema);
joinTableSchema = quote(joinTableSchema, true);
}
} else {
joinTableSchema = schema;
Expand Down Expand Up @@ -538,7 +538,7 @@ String[] buildCreateTableStatements(@NonNull PersistentEntity entity) {
createStatements.add(generatedDefinition);
} else if (isSequence) {
final boolean isSqlServer = dialect == Dialect.SQL_SERVER;
final String sequenceName = quote(unescapedTableName + SEQ_SUFFIX);
final String sequenceName = quote(unescapedTableName + SEQ_SUFFIX, true);
String createSequenceStmt = "CREATE SEQUENCE " + sequenceName;
if (isSqlServer) {
createSequenceStmt += " AS BIGINT";
Expand Down Expand Up @@ -1215,10 +1215,10 @@ private String[] asStringPath(List<Association> associations, PersistentProperty
private String getSequenceStatement(String unescapedTableName, PersistentProperty property) {
final String sequenceName = resolveSequenceName(property, unescapedTableName);
return switch (dialect) {
case ORACLE -> quote(sequenceName) + ".nextval";
case ORACLE -> quote(sequenceName, true) + ".nextval";
case POSTGRES -> "nextval('" + sequenceName + "')";
case H2 -> "nextval('" + sequenceName + "')";
case SQL_SERVER -> "NEXT VALUE FOR " + quote(sequenceName);
case SQL_SERVER -> "NEXT VALUE FOR " + quote(sequenceName, true);
default -> throw new IllegalStateException("Cannot generate a sequence for dialect: " + dialect);
};
}
Expand Down Expand Up @@ -1307,12 +1307,12 @@ public String getTableName(PersistentEntity entity) {
String schema = getSchemaName(entity);
if (StringUtils.isNotEmpty(schema)) {
if (escape) {
return quote(schema) + '.' + quote(tableName);
return quote(schema, true) + '.' + quote(tableName, true);
} else {
return schema + '.' + tableName;
}
} else {
return escape ? quote(tableName) : tableName;
return escape ? quote(tableName, true) : tableName;
}
}

Expand Down Expand Up @@ -1504,15 +1504,15 @@ protected void buildJoin(String joinType,
.stringValue(ANN_JOIN_TABLE, SqlMembers.SCHEMA)
.orElse(getSchemaName(associationOwner));
if (StringUtils.isNotEmpty(joinTableSchema) && escape) {
joinTableSchema = quote(joinTableSchema);
joinTableSchema = quote(joinTableSchema, true);
}
String joinTableName = annotationMetadata
.stringValue(ANN_JOIN_TABLE, "name")
.orElseGet(() -> getMappedName(namingStrategy, association));
String joinTableAlias = annotationMetadata
.stringValue(ANN_JOIN_TABLE, "alias")
.orElseGet(() -> currentJoinAlias + joinTableName + "_");
String finalTableName = escape ? quote(joinTableName) : joinTableName;
String finalTableName = escape ? quote(joinTableName, true) : joinTableName;
if (StringUtils.isNotEmpty(joinTableSchema)) {
finalTableName = joinTableSchema + DOT + finalTableName;
}
Expand Down Expand Up @@ -1708,20 +1708,16 @@ private <T> List<T> merge(List<T> left, List<T> right) {
return associations;
}

/**
* Quote a column name for the dialect.
*
* @param persistedName The persisted name.
* @return The quoted name
*/
@Override
protected String quote(String persistedName) {
protected String quote(String persistedName, boolean supportsDynamicValues) {
return switch (dialect) {
case MYSQL, H2 -> '`' + persistedName + '`';
case SQL_SERVER -> '[' + persistedName + ']';
case ORACLE ->
case ORACLE -> {
// Oracle requires quoted identifiers to be in upper case
'"' + persistedName.toUpperCase(Locale.ENGLISH) + '"';
String result = supportsDynamicValues ? SqlQueryBuilderUtils.mapPersistedName(persistedName, s -> s.toUpperCase(Locale.ENGLISH)) : persistedName.toUpperCase(Locale.ENGLISH);
yield '"' + result + '"';
}
default -> '"' + persistedName + '"';
};
}
Expand Down
Loading
Loading