or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

connection-pooling.mddatabase-configuration.mddatabase-management.mddatabase-operations.mddependency-injection.mdindex.md

dependency-injection.mddocs/

0

# Dependency Injection

1

2

Runtime and compile-time dependency injection modules with support for named databases, lifecycle management, and both Guice and manual DI approaches.

3

4

## Capabilities

5

6

### Runtime Dependency Injection

7

8

Guice-based dependency injection modules for runtime binding.

9

10

```scala { .api }

11

/** DB runtime inject module */

12

final class DBModule extends SimpleModule((environment, configuration) => {

13

// Creates bindings for DBApi and named databases

14

})

15

16

/** HikariCP runtime inject module */

17

class HikariCPModule extends SimpleModule(bind[ConnectionPool].to[HikariCPConnectionPool])

18

```

19

20

**Usage Examples:**

21

22

```scala

23

import play.api.db._

24

import play.api.inject.guice.GuiceApplicationBuilder

25

26

// Enable DB module in Play application

27

val app = new GuiceApplicationBuilder()

28

.configure(

29

"db.default.driver" -> "org.h2.Driver",

30

"db.default.url" -> "jdbc:h2:mem:test"

31

)

32

.build()

33

34

// Modules are automatically loaded via play.modules.enabled

35

```

36

37

**Application Configuration:**

38

39

```hocon

40

# conf/application.conf

41

play.modules.enabled += "play.api.db.DBModule"

42

play.modules.enabled += "play.api.db.HikariCPModule"

43

44

# Or disable if not using databases

45

play.modules.disabled += "play.api.db.DBModule"

46

```

47

48

### Compile-Time Dependency Injection

49

50

Traits for compile-time dependency injection without Guice.

51

52

```scala { .api }

53

/** DB components (for compile-time injection) */

54

trait DBComponents {

55

def environment: Environment

56

def configuration: Configuration

57

def connectionPool: ConnectionPool

58

def applicationLifecycle: ApplicationLifecycle

59

60

lazy val dbApi: DBApi

61

}

62

63

/** HikariCP components (for compile-time injection) */

64

trait HikariCPComponents {

65

def environment: Environment

66

67

lazy val connectionPool: ConnectionPool

68

}

69

```

70

71

**Usage Examples:**

72

73

```scala

74

import play.api.db._

75

import play.api._

76

import play.api.inject.DefaultApplicationLifecycle

77

78

// Implement compile-time DI components

79

class MyApplicationComponents extends HikariCPComponents with DBComponents {

80

val environment = Environment.simple()

81

val configuration = Configuration.load(environment)

82

val applicationLifecycle = new DefaultApplicationLifecycle()

83

84

// HikariCPComponents provides connectionPool

85

// DBComponents provides dbApi

86

87

// Use dbApi in your application

88

lazy val userRepository = new UserRepository(dbApi.database("default"))

89

}

90

91

// Use in application loader

92

class MyApplicationLoader extends ApplicationLoader {

93

def load(context: ApplicationLoader.Context): Application = {

94

val components = new MyApplicationComponents()

95

// Build application with components

96

}

97

}

98

```

99

100

### Database Injection (Scala)

101

102

Direct injection of Database instances.

103

104

```scala { .api }

105

// Inject default database

106

class UserController @Inject()(db: Database) {

107

// Use db instance

108

}

109

110

// Inject DBApi for multiple databases

111

class MultiDbController @Inject()(dbApi: DBApi) {

112

val userDb = dbApi.database("users")

113

val logDb = dbApi.database("logs")

114

}

115

```

116

117

**Usage Examples:**

118

119

```scala

120

import play.api.db._

121

import play.api.mvc._

122

import javax.inject.Inject

123

124

// Single database injection

125

class UserController @Inject()(

126

cc: ControllerComponents,

127

db: Database

128

) extends AbstractController(cc) {

129

130

def getUser(id: Long) = Action {

131

val name = db.withConnection { implicit conn =>

132

val stmt = conn.prepareStatement("SELECT name FROM users WHERE id = ?")

133

stmt.setLong(1, id)

134

val rs = stmt.executeQuery()

135

if (rs.next()) rs.getString("name") else "Unknown"

136

}

137

Ok(name)

138

}

139

}

140

141

// Multiple database injection via DBApi

142

class AnalyticsController @Inject()(

143

cc: ControllerComponents,

144

dbApi: DBApi

145

) extends AbstractController(cc) {

146

147

def generateReport() = Action {

148

val userDb = dbApi.database("users")

149

val analyticsDb = dbApi.database("analytics")

150

151

val userCount = userDb.withConnection { implicit conn =>

152

val stmt = conn.prepareStatement("SELECT COUNT(*) FROM users")

153

val rs = stmt.executeQuery()

154

rs.next()

155

rs.getInt(1)

156

}

157

158

analyticsDb.withConnection { implicit conn =>

159

val stmt = conn.prepareStatement("INSERT INTO reports (user_count, generated_at) VALUES (?, ?)")

160

stmt.setInt(1, userCount)

161

stmt.setTimestamp(2, new java.sql.Timestamp(System.currentTimeMillis()))

162

stmt.executeUpdate()

163

}

164

165

Ok(s"Report generated: $userCount users")

166

}

167

}

168

```

169

170

### Named Database Injection (Java)

171

172

Inject specific databases by name using the @NamedDatabase annotation.

173

174

```java { .api }

175

@Qualifier

176

@Retention(RetentionPolicy.RUNTIME)

177

public @interface NamedDatabase {

178

String value();

179

}

180

```

181

182

**Usage Examples:**

183

184

```java

185

import play.db.*;

186

import javax.inject.Inject;

187

188

public class UserService {

189

private final Database userDb;

190

private final Database sessionDb;

191

private final DBApi dbApi;

192

193

@Inject

194

public UserService(

195

@NamedDatabase("users") Database userDb,

196

@NamedDatabase("sessions") Database sessionDb,

197

DBApi dbApi

198

) {

199

this.userDb = userDb;

200

this.sessionDb = sessionDb;

201

this.dbApi = dbApi;

202

}

203

204

public void createUser(String username, String email) {

205

userDb.withTransaction(connection -> {

206

PreparedStatement stmt = connection.prepareStatement(

207

"INSERT INTO users (username, email, created_at) VALUES (?, ?, ?)"

208

);

209

stmt.setString(1, username);

210

stmt.setString(2, email);

211

stmt.setTimestamp(3, new Timestamp(System.currentTimeMillis()));

212

stmt.executeUpdate();

213

});

214

}

215

216

public void createUserSession(long userId, String sessionToken) {

217

sessionDb.withConnection(connection -> {

218

PreparedStatement stmt = connection.prepareStatement(

219

"INSERT INTO user_sessions (user_id, session_token, created_at) VALUES (?, ?, ?)"

220

);

221

stmt.setLong(1, userId);

222

stmt.setString(2, sessionToken);

223

stmt.setTimestamp(3, new Timestamp(System.currentTimeMillis()));

224

stmt.executeUpdate();

225

});

226

}

227

228

// Access all databases when needed

229

public void performMaintenance() {

230

dbApi.getDatabases().forEach(db -> {

231

db.withConnection(connection -> {

232

PreparedStatement stmt = connection.prepareStatement("ANALYZE");

233

stmt.execute();

234

});

235

});

236

}

237

}

238

```

239

240

### Default Database Injection (Java)

241

242

Inject the default database without annotation.

243

244

```java { .api }

245

public class SimpleUserService {

246

private final Database db;

247

248

@Inject

249

public SimpleUserService(Database db) {

250

this.db = db; // Injects the default database

251

}

252

253

public Optional<User> findUser(long id) {

254

return db.withConnection(connection -> {

255

PreparedStatement stmt = connection.prepareStatement("SELECT * FROM users WHERE id = ?");

256

stmt.setLong(1, id);

257

ResultSet rs = stmt.executeQuery();

258

return rs.next() ? Optional.of(mapUser(rs)) : Optional.empty();

259

});

260

}

261

}

262

```

263

264

### Provider Classes

265

266

Provider implementations for dependency injection framework integration.

267

268

```scala { .api }

269

/** Inject provider for DB implementation of DB API */

270

@Singleton

271

class DBApiProvider(

272

environment: Environment,

273

configuration: Configuration,

274

defaultConnectionPool: ConnectionPool,

275

lifecycle: ApplicationLifecycle,

276

maybeInjector: Option[Injector]

277

) extends Provider[DBApi] {

278

lazy val get: DBApi

279

}

280

281

/** Inject provider for named databases */

282

class NamedDatabaseProvider(name: String) extends Provider[Database] {

283

@Inject private var dbApi: DBApi = _

284

lazy val get: Database = dbApi.database(name)

285

}

286

```

287

288

**Usage Examples:**

289

290

```scala

291

import play.api.db._

292

import play.api.inject._

293

294

// Manual provider usage (typically not needed)

295

val provider = new NamedDatabaseProvider("users")

296

// Inject dbApi into provider

297

val database = provider.get

298

```

299

300

### Module Configuration

301

302

Configure database modules and bindings in application configuration.

303

304

```hocon

305

# Enable/disable database modules

306

play.modules.enabled += "play.api.db.DBModule"

307

play.modules.enabled += "play.api.db.HikariCPModule"

308

309

# Disable modules if not using databases

310

play.modules.disabled += "play.api.db.DBModule"

311

312

# Database binding configuration

313

play.db.config = "db" # Configuration key for databases

314

play.db.default = "default" # Name of default database

315

play.db.pool = "hikaricp" # Default connection pool

316

317

# Multiple database configurations create named bindings

318

db {

319

default {

320

driver = "org.h2.Driver"

321

url = "jdbc:h2:mem:default"

322

}

323

324

users {

325

driver = "org.postgresql.Driver"

326

url = "jdbc:postgresql://localhost/users"

327

}

328

329

sessions {

330

driver = "org.postgresql.Driver"

331

url = "jdbc:postgresql://localhost/sessions"

332

}

333

}

334

```

335

336

## Lifecycle Management

337

338

### Automatic Lifecycle

339

340

When using dependency injection, database lifecycle is managed automatically.

341

342

```scala { .api }

343

// DBApiProvider automatically handles lifecycle

344

class DBApiProvider(..., lifecycle: ApplicationLifecycle, ...) extends Provider[DBApi] {

345

lazy val get: DBApi = {

346

val db = new DefaultDBApi(...)

347

348

// Register shutdown hook

349

lifecycle.addStopHook { () =>

350

Future.fromTry(Try(db.shutdown()))

351

}

352

353

// Initialize databases

354

db.initialize(logInitialization = environment.mode != Mode.Test)

355

db

356

}

357

}

358

```

359

360

### Manual Lifecycle

361

362

For compile-time DI or manual management:

363

364

```scala

365

import play.api.db._

366

367

// Create and initialize

368

val dbApi = new DefaultDBApi(configurations, connectionPool, environment)

369

dbApi.initialize(logInitialization = true)

370

371

try {

372

// Use databases

373

val db = dbApi.database("default")

374

// ... application logic

375

} finally {

376

// Shutdown when done

377

dbApi.shutdown()

378

}

379

```

380

381

## Testing with Dependency Injection

382

383

### Test Database Injection

384

385

```scala

386

import play.api.db._

387

import play.api.test._

388

import org.scalatest.BeforeAndAfterAll

389

390

class DatabaseSpec extends PlaySpec with BeforeAndAfterAll {

391

392

"Database injection" should {

393

"inject test database" in new WithApplication(

394

_.configure(

395

"db.default.driver" -> "org.h2.Driver",

396

"db.default.url" -> "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1"

397

)

398

) {

399

val db = app.injector.instanceOf[Database]

400

401

db.withConnection { implicit conn =>

402

conn.prepareStatement("CREATE TABLE test (id INT)").execute()

403

conn.prepareStatement("INSERT INTO test VALUES (1)").execute()

404

405

val rs = conn.prepareStatement("SELECT COUNT(*) FROM test").executeQuery()

406

rs.next()

407

rs.getInt(1) must equal(1)

408

}

409

}

410

}

411

}

412

```

413

414

### Java Test Example

415

416

```java

417

import play.db.*;

418

import play.test.*;

419

import org.junit.Test;

420

import javax.inject.Inject;

421

422

public class DatabaseTest extends WithApplication {

423

424

@Inject Database db;

425

426

@Test

427

public void testDatabaseInjection() {

428

Integer count = db.withConnection(connection -> {

429

PreparedStatement stmt = connection.prepareStatement("SELECT 1");

430

ResultSet rs = stmt.executeQuery();

431

return rs.next() ? 1 : 0;

432

});

433

434

assertEquals(Integer.valueOf(1), count);

435

}

436

}

437

```

438

439

## Error Handling

440

441

### Injection Errors

442

443

- **Missing configuration**: Thrown when database configuration is missing for named databases

444

- **Circular dependencies**: Prevented by lazy initialization in providers

445

- **Module conflicts**: Thrown when conflicting modules are enabled

446

447

### Provider Errors

448

449

- **Initialization failures**: Thrown during application startup if database cannot be initialized

450

- **Connection failures**: Logged but don't prevent application startup (use `initialize()` instead of deprecated `connect()`)

451

452

## Best Practices

453

454

### Module Selection

455

456

```hocon

457

# Use specific modules based on needs

458

play.modules.enabled += "play.api.db.DBModule" # Always needed for databases

459

play.modules.enabled += "play.api.db.HikariCPModule" # Use for HikariCP (recommended)

460

461

# Alternative connection pools

462

play.modules.enabled += "play.api.db.CustomPoolModule" # Custom implementation

463

```

464

465

### Named Database Organization

466

467

```scala

468

// Organize by domain/purpose

469

@NamedDatabase("users") Database userDb

470

@NamedDatabase("analytics") Database analyticsDb

471

@NamedDatabase("cache") Database cacheDb

472

473

// Rather than generic names

474

@NamedDatabase("db1") Database db1 // Avoid

475

@NamedDatabase("db2") Database db2 // Avoid

476

```

477

478

### Compile-Time vs Runtime DI

479

480

```scala

481

// Use compile-time DI for:

482

// - Better performance (no reflection)

483

// - Compile-time error checking

484

// - Smaller application size

485

486

// Use runtime DI for:

487

// - Development convenience

488

// - Plugin/module systems

489

// - Dynamic configuration

490

```