Skip to content

Commit 97557fc

Browse files
authored
Feature/storm 25 (#26)
* Eliminate annotations from metamodel. * Add nullable reference method. * Document and clean up conversion logic.
1 parent cf1ca13 commit 97557fc

File tree

33 files changed

+223
-153
lines changed

33 files changed

+223
-153
lines changed

README.adoc

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,15 @@ Maven::
4545
<dependency>
4646
<groupId>st.orm</groupId>
4747
<artifactId>storm</artifactId>
48-
<version>1.3.4</version>
48+
<version>1.3.5</version>
4949
<scope>compile</scope>
5050
</dependency>
5151
----
5252
Gradle::
5353
+
5454
[source,groovy]
5555
----
56-
implementation 'st.orm:storm:1.3.4'
56+
implementation 'st.orm:storm:1.3.5'
5757
----
5858
====
5959

@@ -846,15 +846,15 @@ Maven::
846846
<dependency>
847847
<groupId>st.orm</groupId>
848848
<artifactId>storm-oracle</artifactId>
849-
<version>1.3.4</version>
849+
<version>1.3.5</version>
850850
<scope>runtime</scope>
851851
</dependency>
852852
----
853853
Gradle::
854854
+
855855
[source,groovy]
856856
----
857-
runtimeOnly 'st.orm:storm-oracle:1.3.4'
857+
runtimeOnly 'st.orm:storm-oracle:1.3.5'
858858
----
859859
====
860860

@@ -876,15 +876,15 @@ Maven::
876876
<dependency>
877877
<groupId>st.orm</groupId>
878878
<artifactId>storm-metamodel-processor</artifactId>
879-
<version>1.3.4</version>
879+
<version>1.3.5</version>
880880
<scope>provided</scope>
881881
</dependency>
882882
----
883883
Gradle::
884884
+
885885
[source,groovy]
886886
----
887-
annotationProcessor 'st.orm:storm-metamodel-processor:1.3.4'
887+
annotationProcessor 'st.orm:storm-metamodel-processor:1.3.5'
888888
----
889889
====
890890

@@ -952,15 +952,15 @@ Maven::
952952
<dependency>
953953
<groupId>st.orm</groupId>
954954
<artifactId>storm-json</artifactId>
955-
<version>1.3.4</version>
955+
<version>1.3.5</version>
956956
<scope>compile</scope>
957957
</dependency>
958958
----
959959
Gradle::
960960
+
961961
[source,groovy]
962962
----
963-
implementation 'st.orm:storm-json:1.3.4'
963+
implementation 'st.orm:storm-json:1.3.5'
964964
----
965965
====
966966

@@ -1096,15 +1096,15 @@ Maven::
10961096
<dependency>
10971097
<groupId>st.orm</groupId>
10981098
<artifactId>storm-spring</artifactId>
1099-
<version>1.3.4</version>
1099+
<version>1.3.5</version>
11001100
<scope>compile</scope>
11011101
</dependency>
11021102
----
11031103
Gradle::
11041104
+
11051105
[source,groovy]
11061106
----
1107-
implementation 'st.orm:storm-spring:1.3.4'
1107+
implementation 'st.orm:storm-spring:1.3.5'
11081108
----
11091109
====
11101110

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
</properties>
1313
<groupId>st.orm</groupId>
1414
<artifactId>storm-framework</artifactId>
15-
<version>1.3.4</version>
15+
<version>1.3.5</version>
1616
<packaging>pom</packaging>
1717
<name>Storm Framework</name>
1818
<description>A SQL Template and ORM framework, focusing on modernizing and simplifying database programming.</description>

storm-json/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<parent>
77
<groupId>st.orm</groupId>
88
<artifactId>storm-framework</artifactId>
9-
<version>1.3.4</version>
9+
<version>1.3.5</version>
1010
<relativePath>../pom.xml</relativePath>
1111
</parent>
1212
<artifactId>storm-json</artifactId>

storm-json/src/main/java/st/orm/json/spi/JsonORMConverterImpl.java

Lines changed: 58 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,14 @@
3939

4040
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES;
4141
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
42+
import static java.util.Objects.requireNonNull;
4243
import static java.util.Optional.empty;
4344

44-
public class JsonORMConverterImpl implements ORMConverter {
45+
/**
46+
* Implementation of {@link ORMConverter} that converts JSON fields to and from the database. It uses Jackson for JSON
47+
* serialization and deserialization.
48+
*/
49+
public final class JsonORMConverterImpl implements ORMConverter {
4550
private static final ORMReflection REFLECTION = Providers.getORMReflection();
4651
private static final Map<CacheKey, ObjectMapper> OBJECT_MAPPER = new ConcurrentHashMap<>();
4752

@@ -51,13 +56,15 @@ public class JsonORMConverterImpl implements ORMConverter {
5156

5257
record CacheKey(Class<?> sealedType, Json json) {}
5358

54-
public JsonORMConverterImpl(@Nonnull RecordComponent component, @Nonnull TypeReference<?> typeReference, @Nonnull Json json) {
55-
this.component = component;
56-
this.typeReference = typeReference;
59+
public JsonORMConverterImpl(@Nonnull RecordComponent component,
60+
@Nonnull TypeReference<?> typeReference,
61+
@Nonnull Json json) {
62+
this.component = requireNonNull(component, "component");
63+
this.typeReference = requireNonNull(typeReference, "typeReference");
5764
var type = getRawType(typeReference.getType())
5865
.filter(Class::isSealed)
5966
.orElse(null);
60-
this.mapper = OBJECT_MAPPER.computeIfAbsent(new CacheKey(type, json), key -> {
67+
this.mapper = OBJECT_MAPPER.computeIfAbsent(new CacheKey(type, requireNonNull(json, "json")), key -> {
6168
var mapper = new ObjectMapper();
6269
mapper.findAndRegisterModules();
6370
if (!json.failOnUnknown()) {
@@ -93,37 +100,72 @@ private static NamedType[] getPermittedSubtypes(Class<?> sealedClass) {
93100
.toArray(NamedType[]::new);
94101
}
95102

103+
/**
104+
* Returns the number of parameters in the SQL template.
105+
*
106+
* <p><strong>Note:</strong> the count must match the parameter as returned by {@link #getParameterTypes()}.</p>
107+
*
108+
* @return the number of parameters.
109+
*/
96110
@Override
97111
public int getParameterCount() {
112+
// The number of parameters is always 1 for this implementation, as it only handles a single JSON field.
98113
return 1;
99114
}
100115

116+
/**
117+
* Returns the types of the parameters in the SQL template.
118+
*
119+
* @return a list of parameter types.
120+
*/
101121
@Override
102122
public List<Class<?>> getParameterTypes() {
103123
return List.of(String.class);
104124
}
105125

106-
@Override
107-
public Object convert(@Nonnull Object[] args) throws SqlTemplateException {
108-
try {
109-
Object arg = args[0];
110-
return arg == null ? null : mapper.readValue((String) args[0], typeReference);
111-
} catch (JsonProcessingException e) {
112-
throw new SqlTemplateException(e);
113-
}
114-
}
115-
126+
/**
127+
* Returns the names of the columns that will be used in the SQL template.
128+
*
129+
* <p><strong>Note:</strong> the names must match the parameters as returned by {@link #getParameterTypes()}.</p>
130+
*
131+
* @return a list of column names.
132+
*/
116133
@Override
117134
public List<Name> getColumns(@Nonnull NameResolver nameResolver) throws SqlTemplateException {
118135
return List.of(nameResolver.getName(component));
119136
}
120137

138+
/**
139+
* Converts the given record to a list of values that can be used in the SQL template.
140+
*
141+
* <p><strong>Note:</strong> the values must match the parameters as returned by {@link #getParameterTypes()}.</p>
142+
*
143+
* @param record the record to convert.
144+
* @return the values to be used in the SQL template.
145+
*/
121146
@Override
122-
public List<Object> getValues(@Nullable Record record) throws SqlTemplateException {
147+
public List<Object> toDatabase(@Nullable Record record) throws SqlTemplateException {
123148
try {
124149
return List.of(mapper.writeValueAsString(record == null ? null : REFLECTION.invokeComponent(component, record)));
125150
} catch (Throwable e) {
126151
throw new SqlTemplateException(e);
127152
}
128153
}
154+
155+
/**
156+
* Converts the given values to an object that is used in the object model.
157+
*
158+
* @param values the arguments to convert. The arguments match the parameters as returned by getParameterTypes().
159+
* @return the converted object.
160+
* @throws SqlTemplateException if an error occurs during conversion.
161+
*/
162+
@Override
163+
public Object fromDatabase(@Nonnull Object[] values) throws SqlTemplateException {
164+
try {
165+
Object value = values[0];
166+
return value == null ? null : mapper.readValue((String) values[0], typeReference);
167+
} catch (JsonProcessingException e) {
168+
throw new SqlTemplateException(e);
169+
}
170+
}
129171
}

storm-json/src/main/java/st/orm/json/spi/JsonORMConverterProviderImpl.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,21 @@
2828

2929
import static java.util.Optional.empty;
3030

31+
/**
32+
* Provides an ORM converter for JSON annotated record components.
33+
*
34+
* <p>This implementation retrieves the {@link Json} annotation from the record component and creates a
35+
* {@link JsonORMConverterImpl} if the annotation is present.</p>
36+
*/
3137
public class JsonORMConverterProviderImpl implements ORMConverterProvider {
3238
private static final ORMReflection REFLECTION = Providers.getORMReflection();
3339

40+
/**
41+
* Retrieves the converter for the specified record component.
42+
*
43+
* @param component the record component for which to get the converter
44+
* @return an Optional containing the ORMConverter if available, or empty if not supported.
45+
*/
3446
@Override
3547
public Optional<ORMConverter> getConverter(@Nonnull RecordComponent component) {
3648
Json json = REFLECTION.getAnnotation(component, Json.class);

storm-kotlin/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<parent>
77
<groupId>st.orm</groupId>
88
<artifactId>storm-framework</artifactId>
9-
<version>1.3.4</version>
9+
<version>1.3.5</version>
1010
<relativePath>../pom.xml</relativePath>
1111
</parent>
1212
<artifactId>storm-kotlin</artifactId>

storm-kotlin/src/main/java/st/orm/kotlin/template/KORMTemplate.java

Lines changed: 8 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -15,100 +15,20 @@
1515
*/
1616
package st.orm.kotlin.template;
1717

18+
import st.orm.kotlin.KTemplates;
1819
import st.orm.kotlin.repository.KRepositoryLookup;
1920
import st.orm.kotlin.template.impl.KORMTemplateImpl;
21+
import st.orm.repository.EntityRepository;
22+
import st.orm.repository.ProjectionRepository;
2023
import st.orm.template.ORMTemplate;
2124

2225
/**
23-
* <p>The {@link KORMTemplate} supports a fluent API that allows you to build queries in a more concise manner:
26+
* <p>The {@code KORMTemplate} is the primary interface that extends the {@code KQueryTemplate} and
27+
* {@code KRepositoryLooking} interfaces, providing access to both the SQL Template engine and ORM logic.
2428
*
25-
* <pre>{@code
26-
* DataSource dataSource = ...;
27-
* List<User> users = KTemplates.ORM(dataSource).entity(User.class)
28-
* .select()
29-
* .where("city", Operator.EQUALS, "Sunnyvale")
30-
* .getResultList();
31-
* }</pre>
32-
*
33-
* <p>In this example, the query is constructed by chaining method calls, enhancing readability and reducing boilerplate code.
34-
* The {@code entity(Class<T> clazz)} method specifies the entity type for the query, and subsequent methods like {@code select()}
35-
* and {@code where()} build upon it.
36-
*
37-
* <h2>Key Features</h2>
38-
* <ul>
39-
* <li><strong>Type Safety:</strong> Leverages Java generics and type inference to ensure that queries are type-safe.</li>
40-
* <li><strong>Fluent API:</strong> Supports method chaining to build queries in a readable and concise manner.</li>
41-
* <li><strong>Flexibility:</strong> Compatible with both JPA and JDBC, allowing you to choose the underlying technology.</li>
42-
* <li><strong>SQL Templates:</strong> Integrates with SQL string templates for dynamic query construction.</li>
43-
* <li><strong>Ease of Use:</strong> Reduces boilerplate code and simplifies common database operations.</li>
44-
* </ul>
45-
*
46-
* <h2>Creating KORMTemplate Instances</h2>
47-
*
48-
* <p>The {@code KTemplates} interface provides static methods to create {@link KORMTemplate} instances based on your data source:
49-
*
50-
* <h3>Using EntityManager (JPA)</h3>
51-
* <pre>{@code
52-
* EntityManager entityManager = ...;
53-
* KORMTemplate orm = Kemplates.ORM(entityManager);
54-
* }</pre>
55-
*
56-
* <h3>Using DataSource (JDBC)</h3>
57-
* <pre>{@code
58-
* DataSource dataSource = ...;
59-
* KORMTemplate orm = KTemplates.ORM(dataSource);
60-
* }</pre>
61-
*
62-
* <h3>Using Connection (JDBC)</h3>
63-
*
64-
* <p><strong>Note:</strong> The caller is responsible for closing the connection after usage.
65-
*
66-
* <pre>{@code
67-
* Connection connection = ...;
68-
* ORMTemplate orm = KTemplates.KORM(connection);
69-
* }</pre>
70-
*
71-
* <h2>Example: Querying with Fluent API</h2>
72-
* <p>Here is a more detailed example demonstrating how to use the fluent API to perform a query:
73-
* <pre>{@code
74-
* DataSource dataSource = ...;
75-
* List<User> users = ORM(dataSource).entity(User.class)
76-
* .select()
77-
* .where("city.name", Operator.EQUALS, "Sunnyvale")
78-
* .getResultList();
79-
* }</pre>
80-
*
81-
* <p>In this example:
82-
* <ul>
83-
* <li>{@code entity(User.class)} specifies the entity to query.</li>
84-
* <li>{@code select()} constructs the SELECT clause.</li>
85-
* <li>{@code where("city", Operator.EQUALS, "Sunnyvale")} adds a WHERE condition.</li>
86-
* <li>{@code getResultList()} executes the query and returns the results as a list.</li>
87-
* </ul>
88-
*
89-
* <h2>Integration with SQL Templates</h2>
90-
* <p>You can seamlessly integrate the fluent API with SQL templates when needed. For instance, you can combine method chaining with template-based queries:
91-
*
92-
* <pre>{@code
93-
* City city = ORM(dataSource).entity(City.class)
94-
* .select()
95-
* .where(RAW."name = \{"Sunnyvale"}")
96-
* .getSingleResult();
97-
* List<User> users = ORM(dataSource).query(RAW."""
98-
* SELECT \{User.class}
99-
* FROM \{User.class}
100-
* WHERE \{city)}""")
101-
* .getResultList(User.class);
102-
* }</pre>
103-
*
104-
* <h2>Conclusion</h2>
105-
*
106-
* <p>The {@code KTemplates} interface streamlines the process of writing database queries by integrating
107-
* object-relational mapping with SQL string templates and a fluent API, ensuring type safety and reducing boilerplate code.
108-
* Whether you prefer constructing queries using SQL templates or method chaining, the {@code KTemplates} interface provides
109-
* flexible options to suit your development style.
110-
*
111-
* @see st.orm.kotlin.KTemplates
29+
* @see KTemplates
30+
* @see EntityRepository
31+
* @see ProjectionRepository
11232
*/
11333
public interface KORMTemplate extends KQueryTemplate, KRepositoryLookup {
11434

storm-kotlin/src/main/java/st/orm/kotlin/template/KQueryBuilder.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -702,7 +702,7 @@ public final KQueryBuilder<T, R, ID> whereRef(@Nonnull Iterable<? extends Ref<T>
702702
}
703703

704704
/**
705-
* Adds WHERE clause that matches the specified record. The record can represent any of the related tables in the
705+
* Adds a WHERE clause that matches the specified record. The record can represent any of the related tables in the
706706
* table graph.
707707
*
708708
* @param path the path to the object in the table graph.
@@ -714,7 +714,7 @@ public final <V extends Record> KQueryBuilder<T, R, ID> where(@Nonnull Metamodel
714714
}
715715

716716
/**
717-
* Adds WHERE clause that matches the specified ref. The ref can represent any of the related tables in the
717+
* Adds a WHERE clause that matches the specified ref. The ref can represent any of the related tables in the
718718
* table graph.
719719
*
720720
* @param path the path to the object in the table graph.

storm-kotlin/src/main/java/st/orm/kotlin/template/impl/KColumnImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ public String name() {
8181
}
8282

8383
/**
84-
* Gets the qualified name of the column including escape characters where necessary.
84+
* Gets the qualified name of the column, including escape characters where necessary.
8585
*
8686
* @param dialect the SQL dialect.
8787
* @return the qualified column name.

0 commit comments

Comments
 (0)