Skip to content

Commit 6ca32df

Browse files
oyvindbergclaude
andauthored
Add IoC framework integration for Spring and Jakarta CDI (#161)
- Add IocFramework enum with Spring and JakartaCdi variants - Add iocFramework option to Options (default None) - Generate @repository annotation on RepoImpl for Spring - Generate @ApplicationScoped annotation on RepoImpl for Jakarta CDI - Generate @Repository/@singleton annotations on TestInsert classes - Add documentation page for IoC frameworks - Add spring-context dependency for Doobie Scala 3 tester - Add jakarta.enterprise.cdi-api dependency for Java tester 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude <noreply@anthropic.com>
1 parent 3acbe64 commit 6ca32df

368 files changed

Lines changed: 886 additions & 16 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

bleep.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ projects:
107107
jvm213:
108108
sources: ./generated-and-checked-in-2.13
109109
jvm3:
110+
dependencies:
111+
- org.springframework:spring-context:6.1.6
110112
sources: ./generated-and-checked-in-3
111113
dependencies:
112114
- io.circe::circe-core:0.14.7
@@ -126,6 +128,7 @@ projects:
126128
- com.fasterxml.jackson.core:jackson-databind:2.17.2
127129
- com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.17.2
128130
- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.17.2
131+
- jakarta.enterprise:jakarta.enterprise.cdi-api:4.0.1
129132
dependsOn: typo-dsl-java
130133
isTestProject: true
131134
java:
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
---
2+
title: IoC Framework Integration
3+
---
4+
5+
Typo can generate annotations for IoC (Inversion of Control) frameworks, making your repositories easy to inject with dependency injection.
6+
7+
## Configuration
8+
9+
Set the `iocFramework` option to enable annotation generation:
10+
11+
```scala
12+
import typo.*
13+
14+
// For Spring
15+
val options = Options(
16+
pkg = "org.foo",
17+
lang = LangScala(Dialect.Scala3, TypeSupportScala),
18+
dbLib = Some(DbLibName.Doobie),
19+
iocFramework = Some(IocFramework.Spring)
20+
)
21+
22+
// For Jakarta CDI (Quarkus, WildFly, etc.)
23+
val javaOptions = Options(
24+
pkg = "org.foo",
25+
lang = LangJava,
26+
dbLib = Some(DbLibName.Typo),
27+
iocFramework = Some(IocFramework.JakartaCdi)
28+
)
29+
```
30+
31+
## Supported Frameworks
32+
33+
### Spring
34+
35+
When `IocFramework.Spring` is configured, Typo adds:
36+
37+
- `@Repository` annotation on `RepoImpl` classes (main scope)
38+
- `@Repository` annotation on `TestInsert` classes (test scope)
39+
40+
**Generated code example:**
41+
42+
```scala
43+
import org.springframework.stereotype.Repository
44+
45+
@Repository
46+
class AddressRepoImpl extends AddressRepo {
47+
// ...
48+
}
49+
```
50+
51+
This enables automatic component scanning and dependency injection:
52+
53+
```scala
54+
@Service
55+
class AddressService @Autowired() (addressRepo: AddressRepo) {
56+
// addressRepo is automatically injected
57+
}
58+
```
59+
60+
### Jakarta CDI (Quarkus)
61+
62+
When `IocFramework.JakartaCdi` is configured, Typo adds:
63+
64+
- `@ApplicationScoped` annotation on `RepoImpl` classes (main scope)
65+
- `@Singleton` annotation on `TestInsert` classes (test scope)
66+
67+
**Generated code example:**
68+
69+
```java
70+
import jakarta.enterprise.context.ApplicationScoped;
71+
72+
@ApplicationScoped
73+
public class AddressRepoImpl implements AddressRepo {
74+
// ...
75+
}
76+
```
77+
78+
This enables CDI injection in Quarkus and other Jakarta EE environments:
79+
80+
```java
81+
@ApplicationScoped
82+
public class AddressService {
83+
@Inject
84+
AddressRepo addressRepo;
85+
86+
// addressRepo is automatically injected
87+
}
88+
```
89+
90+
## Dependencies
91+
92+
You'll need to add the appropriate dependency to your project:
93+
94+
**Spring:**
95+
```
96+
org.springframework:spring-context:6.1.x
97+
```
98+
99+
**Jakarta CDI:**
100+
```
101+
jakarta.enterprise:jakarta.enterprise.cdi-api:4.0.x
102+
```
103+
104+
## Notes
105+
106+
- Mock repositories do not currently receive IoC annotations due to their constructor requirements
107+
- The annotations are only added when `iocFramework` is explicitly set (default is `None`)
108+
- Works with both Scala and Java code generation

site/sidebars.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ const sidebars = {
5555
{type: "doc", id: "other-features/streaming-inserts"},
5656
{type: "doc", id: "other-features/generate-into-multiple-projects"},
5757
{type: "doc", id: "other-features/json"},
58+
{type: "doc", id: "other-features/ioc-frameworks"},
5859
{type: "doc", id: "other-features/faster-compilation"},
5960
{type: "doc", id: "other-features/flexible"},
6061
{type: "doc", id: "other-features/clickable-links"},

typo-scripts/src/scala/scripts/GeneratedAdventureWorks.scala

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,21 +40,21 @@ object GeneratedAdventureWorks {
4040
),
4141
Duration.Inf
4242
)
43-
val variants: Seq[(Lang, DbLibName, Option[JsonLibName], String, String)] = List(
44-
(LangScala(Dialect.Scala2XSource3, TypeSupportScala), DbLibName.Anorm, Some(JsonLibName.PlayJson), "typo-tester-anorm", "-2.13"),
45-
(LangScala(Dialect.Scala3, TypeSupportScala), DbLibName.Anorm, Some(JsonLibName.PlayJson), "typo-tester-anorm", "-3"),
46-
(LangScala(Dialect.Scala2XSource3, TypeSupportScala), DbLibName.Doobie, Some(JsonLibName.Circe), "typo-tester-doobie", "-2.13"),
47-
(LangScala(Dialect.Scala3, TypeSupportScala), DbLibName.Doobie, Some(JsonLibName.Circe), "typo-tester-doobie", "-3"),
48-
(LangScala(Dialect.Scala2XSource3, TypeSupportScala), DbLibName.ZioJdbc, Some(JsonLibName.ZioJson), "typo-tester-zio-jdbc", "-2.13"),
49-
(LangScala(Dialect.Scala3, TypeSupportScala), DbLibName.ZioJdbc, Some(JsonLibName.ZioJson), "typo-tester-zio-jdbc", "-3"),
50-
(LangJava, DbLibName.Typo, Some(JsonLibName.Jackson), "typo-tester-typo-java", ""),
51-
(LangScala(Dialect.Scala3, TypeSupportJava), DbLibName.Typo, None, "typo-tester-typo-scala", "")
43+
val variants: Seq[(Lang, DbLibName, Option[JsonLibName], Option[IocFramework], String, String)] = List(
44+
(LangScala(Dialect.Scala2XSource3, TypeSupportScala), DbLibName.Anorm, Some(JsonLibName.PlayJson), None, "typo-tester-anorm", "-2.13"),
45+
(LangScala(Dialect.Scala3, TypeSupportScala), DbLibName.Anorm, Some(JsonLibName.PlayJson), None, "typo-tester-anorm", "-3"),
46+
(LangScala(Dialect.Scala2XSource3, TypeSupportScala), DbLibName.Doobie, Some(JsonLibName.Circe), None, "typo-tester-doobie", "-2.13"),
47+
(LangScala(Dialect.Scala3, TypeSupportScala), DbLibName.Doobie, Some(JsonLibName.Circe), Some(IocFramework.Spring), "typo-tester-doobie", "-3"),
48+
(LangScala(Dialect.Scala2XSource3, TypeSupportScala), DbLibName.ZioJdbc, Some(JsonLibName.ZioJson), None, "typo-tester-zio-jdbc", "-2.13"),
49+
(LangScala(Dialect.Scala3, TypeSupportScala), DbLibName.ZioJdbc, Some(JsonLibName.ZioJson), None, "typo-tester-zio-jdbc", "-3"),
50+
(LangJava, DbLibName.Typo, Some(JsonLibName.Jackson), Some(IocFramework.JakartaCdi), "typo-tester-typo-java", ""),
51+
(LangScala(Dialect.Scala3, TypeSupportJava), DbLibName.Typo, None, None, "typo-tester-typo-scala", "")
5252
)
5353

5454
def go(): Unit = {
5555
val newSqlScripts = Await.result(readSqlFileDirectories(typoLogger, scriptsPath, ds), Duration.Inf)
5656

57-
variants.foreach { case (lang, dbLib, jsonLib, projectPath, suffix) =>
57+
variants.foreach { case (lang, dbLib, jsonLib, iocFramework, projectPath, suffix) =>
5858
val options = Options(
5959
pkg = "adventureworks",
6060
lang = lang,
@@ -69,7 +69,8 @@ object GeneratedAdventureWorks {
6969
enablePrimaryKeyType = !Selector.relationNames("billofmaterials"),
7070
enableTestInserts = Selector.All,
7171
readonlyRepo = Selector.relationNames("purchaseorderdetail"),
72-
enableDsl = true
72+
enableDsl = true,
73+
iocFramework = iocFramework
7374
)
7475
val targetSources = buildDir.resolve(s"$projectPath/generated-and-checked-in$suffix")
7576

typo-tester-doobie/generated-and-checked-in-3/adventureworks/TestInsert.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,8 +328,10 @@ import java.time.LocalDate
328328
import java.time.LocalDateTime
329329
import java.time.LocalTime
330330
import java.time.ZoneOffset
331+
import org.springframework.stereotype.Repository
331332
import scala.util.Random
332333

334+
@Repository
333335
/** Methods to generate random data for `Ident(TestInsert)` */
334336
case class TestInsert(
335337
random: Random,

typo-tester-doobie/generated-and-checked-in-3/adventureworks/hr/d/DViewRepoImpl.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ package adventureworks.hr.d
77

88
import doobie.free.connection.ConnectionIO
99
import fs2.Stream
10+
import org.springframework.stereotype.Repository
1011
import typo.dsl.SelectBuilder
1112
import doobie.syntax.string.toSqlInterpolator
1213

14+
@Repository
1315
class DViewRepoImpl extends DViewRepo {
1416
def select: SelectBuilder[DViewFields, DViewRow] = SelectBuilder.of(""""hr"."d"""", DViewFields.structure, DViewRow.read)
1517

typo-tester-doobie/generated-and-checked-in-3/adventureworks/hr/e/EViewRepoImpl.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ package adventureworks.hr.e
77

88
import doobie.free.connection.ConnectionIO
99
import fs2.Stream
10+
import org.springframework.stereotype.Repository
1011
import typo.dsl.SelectBuilder
1112
import doobie.syntax.string.toSqlInterpolator
1213

14+
@Repository
1315
class EViewRepoImpl extends EViewRepo {
1416
def select: SelectBuilder[EViewFields, EViewRow] = SelectBuilder.of(""""hr"."e"""", EViewFields.structure, EViewRow.read)
1517

typo-tester-doobie/generated-and-checked-in-3/adventureworks/hr/edh/EdhViewRepoImpl.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ package adventureworks.hr.edh
77

88
import doobie.free.connection.ConnectionIO
99
import fs2.Stream
10+
import org.springframework.stereotype.Repository
1011
import typo.dsl.SelectBuilder
1112
import doobie.syntax.string.toSqlInterpolator
1213

14+
@Repository
1315
class EdhViewRepoImpl extends EdhViewRepo {
1416
def select: SelectBuilder[EdhViewFields, EdhViewRow] = SelectBuilder.of(""""hr"."edh"""", EdhViewFields.structure, EdhViewRow.read)
1517

typo-tester-doobie/generated-and-checked-in-3/adventureworks/hr/eph/EphViewRepoImpl.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ package adventureworks.hr.eph
77

88
import doobie.free.connection.ConnectionIO
99
import fs2.Stream
10+
import org.springframework.stereotype.Repository
1011
import typo.dsl.SelectBuilder
1112
import doobie.syntax.string.toSqlInterpolator
1213

14+
@Repository
1315
class EphViewRepoImpl extends EphViewRepo {
1416
def select: SelectBuilder[EphViewFields, EphViewRow] = SelectBuilder.of(""""hr"."eph"""", EphViewFields.structure, EphViewRow.read)
1517

typo-tester-doobie/generated-and-checked-in-3/adventureworks/hr/jc/JcViewRepoImpl.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ package adventureworks.hr.jc
77

88
import doobie.free.connection.ConnectionIO
99
import fs2.Stream
10+
import org.springframework.stereotype.Repository
1011
import typo.dsl.SelectBuilder
1112
import doobie.syntax.string.toSqlInterpolator
1213

14+
@Repository
1315
class JcViewRepoImpl extends JcViewRepo {
1416
def select: SelectBuilder[JcViewFields, JcViewRow] = SelectBuilder.of(""""hr"."jc"""", JcViewFields.structure, JcViewRow.read)
1517

0 commit comments

Comments
 (0)