Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make supported naming strategies available at runtime for native image #2395

Merged
merged 5 commits into from
Jul 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,13 @@
import io.micronaut.core.annotation.Nullable;
import io.micronaut.core.annotation.AnnotationMetadata;
import io.micronaut.core.annotation.AnnotationMetadataProvider;
import io.micronaut.core.beans.BeanIntrospection;
import io.micronaut.core.reflect.ClassUtils;
import io.micronaut.core.reflect.InstantiationUtils;
import io.micronaut.data.annotation.MappedEntity;
import io.micronaut.data.model.naming.NamingStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.Optional;
Expand All @@ -35,6 +39,8 @@
*/
public abstract class AbstractPersistentEntity implements PersistentEntity {

private static final Logger LOG = LoggerFactory.getLogger(AbstractPersistentEntity.class);

private static final Map<String, NamingStrategy> NAMING_STRATEGIES = new ConcurrentHashMap<>(3);

private final AnnotationMetadataProvider annotationMetadataProvider;
Expand Down Expand Up @@ -70,6 +76,21 @@ private static Optional<NamingStrategy> getNamingStrategy(String className, Clas
if (namingStrategy != null) {
return Optional.of(namingStrategy);
} else {
try {
Class<?> namingStrategyClass = ClassUtils.forName(className, classLoader).orElse(null);
if (namingStrategyClass != null) {
BeanIntrospection<?> beanIntrospection = BeanIntrospection.getIntrospection(namingStrategyClass);
Object o = beanIntrospection.instantiate();
if (o instanceof NamingStrategy ns) {
NAMING_STRATEGIES.put(className, ns);
return Optional.of(ns);
}
}
} catch (Exception e) {
if (LOG.isDebugEnabled()) {
LOG.debug("Tried, but could not instantiate naming strategy: {}", className, e);
}
}
Object o = InstantiationUtils.tryInstantiate(className, classLoader).orElse(null);
if (o instanceof NamingStrategy ns) {
NAMING_STRATEGIES.put(className, ns);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package io.micronaut.data.model.naming;

import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.TypeHint;
import io.micronaut.core.naming.NameUtils;

import java.util.Locale;
Expand All @@ -30,6 +31,7 @@ public class NamingStrategies {
/**
* Example: FOO_BAR.
*/
@TypeHint(UnderScoreSeparatedUpperCase.class)
public static class UnderScoreSeparatedUpperCase implements NamingStrategy {
@NonNull
@Override
Expand All @@ -40,6 +42,7 @@ public String mappedName(@NonNull String name) {
/**
* Example: foo_bar.
*/
@TypeHint(UnderScoreSeparatedLowerCase.class)
public static class UnderScoreSeparatedLowerCase implements NamingStrategy {
@NonNull
@Override
Expand All @@ -50,6 +53,7 @@ public String mappedName(@NonNull String name) {
/**
* Example: foo-bar.
*/
@TypeHint(KebabCase.class)
public static class KebabCase implements NamingStrategy {
@NonNull
@Override
Expand All @@ -60,6 +64,7 @@ public String mappedName(@NonNull String name) {
/**
* Example: foobar.
*/
@TypeHint(LowerCase.class)
public static class LowerCase implements NamingStrategy {
@NonNull
@Override
Expand All @@ -70,6 +75,7 @@ public String mappedName(@NonNull String name) {
/**
* Example: foobar.
*/
@TypeHint(UpperCase.class)
public static class UpperCase implements NamingStrategy {
@NonNull
@Override
Expand All @@ -80,6 +86,7 @@ public String mappedName(@NonNull String name) {
/**
* No naming conversion.
*/
@TypeHint(Raw.class)
public static class Raw implements NamingStrategy {
@NonNull
@Override
Expand Down
29 changes: 15 additions & 14 deletions doc-examples/jdbc-example-java/src/main/java/example/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@
package example;

import io.micronaut.data.annotation.*;
import io.micronaut.data.model.naming.NamingStrategies;

@MappedEntity
@Where("@.enabled = true") // <1>
@MappedEntity(namingStrategy = NamingStrategies.Raw.class)
@Where("@.userEnabled = true") // <1>
public class User {
@GeneratedValue
@Id
private Long id;
private String name;
private boolean enabled = true; // <2>
private String userName;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change fields to verify Raw strategy is used in runtime

private boolean userEnabled = true; // <2>

public User(String name) {
this.name = name;
public User(String userName) {
this.userName = userName;
}

public Long getId() {
Expand All @@ -24,19 +25,19 @@ public void setId(Long id) {
this.id = id;
}

public String getName() {
return name;
public String getUserName() {
return userName;
}

public void setName(String name) {
this.name = name;
public void setUserName(String userName) {
this.userName = userName;
}

public boolean isEnabled() {
return enabled;
public boolean isUserEnabled() {
return userEnabled;
}

public void setEnabled(boolean enabled) {
this.enabled = enabled;
public void setUserEnabled(boolean userEnabled) {
this.userEnabled = userEnabled;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
public interface UserRepository extends CrudRepository<User, Long> { // <1>

@Override
@Query("UPDATE user SET enabled = false WHERE id = :id") // <2>
@Query("UPDATE user SET userEnabled = false WHERE id = :id") // <2>
void deleteById(@NonNull @NotNull Long id);

@Query("SELECT * FROM user WHERE enabled = false") // <3>
@Query("SELECT * FROM user WHERE userEnabled = false") // <3>
List<User> findDisabled();
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,6 @@ void testSoftDelete() {

final List<User> disabled = userRepository.findDisabled();
assertEquals(1, disabled.size());
assertEquals("Joe", disabled.iterator().next().getName());
assertEquals("Joe", disabled.iterator().next().getUserName());
}
}
2 changes: 2 additions & 0 deletions src/main/docs/guide/dbc/sqlMapping/sqlNaming.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ include::data-tck/src/main/java/io/micronaut/data/tck/entities/CountryRegion.jav

Few important things to note. Since Micronaut Data pre-computes the table and column name mappings at compilation time the specified api:data.model.naming.NamingStrategy[] implementation must be on the annotation processor classpath (`annotationProcessor` scope for Java or `kapt` for Kotlin).

If running project in native image, custom naming strategy needs to have `io.micronaut.core.annotation.TypeHint(CustomNamingStrategy.class)` annotation where custom naming strategy class is `CustomNamingStrategy`.

In addition if you don't want to repeat the above annotation definition on every entity it is handy to define a meta-annotation where the above annotation definition is applied to another annotation that you add to your class.

==== Escaping Table/Column Name Identifiers
Expand Down