JDBCの基本設定と自動構成の仕組み
Spring Bootでは、リレーショナルデータベース(SQL)およびNoSQLへのアクセスをSpring Dataのアーキテクチャを通じて抽象化・統一しています。JDBCを利用する場合、以下の依存関係をプロジェクトに追加します。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
接続情報の設定はapplication.ymlで行います。タイムゾーンや文字コードの指定を含めた標準的な設定例を示します。
spring:
datasource:
username: db_admin
password: secure_password
url: jdbc:mysql://127.0.0.1:3306/app_db?serverTimezone=Asia/Tokyo&useUnicode=true&characterEncoding=utf-8
driver-class-name: com.mysql.cj.jdbc.Driver
デフォルトでは、Tomcat JDBCプールがデータソースとして採用されます。関連するプロパティはDataSourcePropertiesクラスで管理されています。
自動構成の内部動作
データソースの自動構成はorg.springframework.boot.autoconfigure.jdbc.DataSourceConfigurationクラスで定義されています。条件注釈(@ConditionalOnClass、@ConditionalOnPropertyなど)により、クラスパスに存在するライブラリに応じて最適な接続プールが選択されます。
abstract class DataSourceConfiguration {
protected <T> T buildDataSource(DataSourceProperties props, Class<? extends DataSource> targetClass) {
return (T) props.initializeDataSourceBuilder().type(targetClass).build();
}
@ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "org.apache.tomcat.jdbc.pool.DataSource", matchIfMissing = true)
static class TomcatPool extends DataSourceConfiguration {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.tomcat")
public org.apache.tomcat.jdbc.pool.DataSource resolveDataSource(DataSourceProperties props) {
org.apache.tomcat.jdbc.pool.DataSource ds = buildDataSource(props, org.apache.tomcat.jdbc.pool.DataSource.class);
String validationSql = DatabaseDriver.fromJdbcUrl(props.determineUrl()).getValidationQuery();
if (validationSql != null) {
ds.setTestOnBorrow(true);
ds.setValidationQuery(validationSql);
}
return ds;
}
}
}
HikariCP、DBCP2、Druidなどの別実装を使用したい場合は、spring.datasource.typeプロパティで完全修飾クラス名を指定することで上書き可能です。指定された型はDataSourceBuilder内のリフレクション処理によってインスタンス化され、プロパティがバインドされます。
Druidコネクションプールの導入と監視機能の有効化
Alibabaが開発したDruidは、高性能なコネクションプールであると同時に、SQL実行状況やプール状態を詳細に監視できる機能を標準搭載しています。Spring Boot 2.x以降のデフォルトはHikariCPですが、typeプロパティでDruidへ切り替えられます。
プロパティ設定
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/app_db?serverTimezone=Asia/Tokyo&characterEncoding=utf-8
username: db_admin
password: secure_password
# プール基本設定
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
# 検証・維持設定
validationQuery: SELECT 1
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
# 監視・セキュリティフィルター
filters: stat,wall,log4j
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
監視コンソールとWeb統計の有効化
Druidの管理画面とリクエスト統計を有効化するには、設定クラスでServletとFilterを明示的にBean登録します。
@Configuration
public class DruidMonitorConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource druidDataSource() {
return new DruidDataSource();
}
@Bean
public ServletRegistrationBean<StatViewServlet> druidStatViewServlet() {
ServletRegistrationBean<StatViewServlet> registration = new ServletRegistrationBean<>(new StatViewServlet(), "/sys/druid/*");
Map<String, String> servletParams = new HashMap<>();
servletParams.put("loginUsername", "monitor_admin");
servletParams.put("loginPassword", "monitor_pass");
servletParams.put("allow", ""); // 全IP許可
servletParams.put("deny", "192.168.10.99"); // 特定IP拒否
registration.setInitParameters(servletParams);
return registration;
}
@Bean
public FilterRegistrationBean<WebStatFilter> druidWebStatFilter() {
FilterRegistrationBean<WebStatFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new WebStatFilter());
Map<String, String> filterParams = new HashMap<>();
filterParams.put("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/sys/druid/*");
registration.setInitParameters(filterParams);
registration.addUrlPatterns("/*");
return registration;
}
}
設定完了後、http://localhost:8080/sys/druid/login.htmlへアクセスすることで、接続プールの状態や実行されたSQLの統計情報を確認できます。主要な設定項目としては、初期接続数(initialSize)、最大アクティブ数(maxActive)、接続検証クエリ(validationQuery)、および監視用フィルター(filters)などが挙げられます。これらのパラメータを適切に調整することで、本番環境での安定性と可観測性を確保できます。
MyBatisとの連携設定
Spring Boot公式のスターターは提供されていませんが、MyBatisコミュニティが提供するmybatis-spring-boot-starterを利用することで簡単に統合できます。
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
application.ymlでは、データソース情報に加え、型エイリアスのパッケージスキャン、Mapper XMLの配置場所、およびキャメルケース変換の有効化などを定義します。
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/app_db?serverTimezone=Asia/Tokyo
username: db_admin
password: secure_password
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
type-aliases-package: com.example.domain.model
mapper-locations: classpath:mappings/**/*.xml
config-location: classpath:mybatis/custom-config.xml
configuration:
map-underscore-to-camel-case: true
Mapperインターフェースは、アノテーションベースでSQLを直接記述する方法と、XMLファイルに分離する方法の両方をサポートしています。以下の例ではアノテーションを用いたCRUD操作を示しています。
@Mapper
public interface EmployeeMapper {
@Select("SELECT * FROM employee WHERE id = #{id}")
Employee selectById(Integer id);
@Delete("DELETE FROM employee WHERE id = #{id}")
int deleteById(Integer id);
@Insert("INSERT INTO employee(name, department) VALUES(#{name}, #{department})")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insertRecord(Employee emp);
@Update("UPDATE employee SET name = #{name}, department = #{department} WHERE id = #{id}")
int updateRecord(Employee emp);
}
インターフェースをSpringコンテナに認識させるには、各インターフェースに@Mapperを付与するか、起動クラスで@MapperScan("com.example.persistence")のようにパッケージを指定します。実装の流れは、依存関係の追加、YAMLでのパス設定、Mapperインターフェースの作成、そしてサービス層からの呼び出しという標準的な手順で完了します。
Spring Data JPAの活用とエンティティマッピング
Spring Data JPAは、JPA仕様に基づいたリポジトリパターンを簡潔に実装できるモジュールです。複雑なデータアクセスロジックを大幅に削減できます。
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
エンティティ定義と主鍵戦略
エンティティクラスでは、@Entityや@Tableでテーブルマッピングを定義し、@Idと@GeneratedValueで主鍵戦略を設定します。JPAが標準で提供する生成戦略には、IDENTITY(DBの自動インクリメント)、SEQUENCE、TABLE、AUTOなどがあります。
@Entity
@Table(name = "sys_account")
public class Account {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "login_id", length = 50, nullable = false)
private String loginId;
@Column
private String emailAddress;
// getters and setters
}
要件に応じてカスタムIDジェネレーターを実装することも可能です。IdentifierGeneratorインターフェースを実装し、エンティティ側で@GenericGeneratorと紐付けることで、独自の採番ロジックを適用できます。
public class TimestampIdGenerator implements IdentifierGenerator {
@Override
public Serializable generate(SharedSessionContractImplementor session, Object object) {
return System.currentTimeMillis();
}
}
@Entity
public class Member {
@Id
@GenericGenerator(name = "tsGenerator", strategy = "com.example.infra.TimestampIdGenerator")
@GeneratedValue(generator = "tsGenerator")
private Long memberId;
// other fields...
}
リポジトリインターフェースと自動クエリ生成
データアクセス層はJpaRepositoryを継承するだけで、基本的なCRUDメソッドが自動実装されます。さらに、メソッド名からクエリを自動生成する機能により、SQLを記述せずに複雑な検索も実現できます。
public interface AccountRepository extends JpaRepository<Account, Long> {
List<Account> findByLoginIdContainingAndEmailAddress(String loginPart, String email);
}
JPA動作設定と検証
動作確認用の設定では、ddl-autoでスキーマの自動更新を制御し、show-sqlで実行されるクエリをログ出力します。
spring:
jpa:
hibernate:
ddl-auto: update
show-sql: true
properties:
hibernate:
format_sql: true
テストコードでは、リポジトリを通じてデータの登録、検索、削除を簡潔に実行できます。主キーが存在する場合は更新、不存在の場合は挿入というsave()メソッドの挙動も確認できます。
@SpringBootTest
class JpaIntegrationTest {
@Autowired
private AccountRepository accountRepo;
@Test
void verifyCrudOperations() {
List<Account> allRecords = accountRepo.findAll();
Optional<Account> target = accountRepo.findById(1L);
accountRepo.deleteById(1L);
Account newAcc = new Account();
newAcc.setLoginId("test_user");
newAcc.setEmailAddress("test@example.com");
accountRepo.save(newAcc);
List<Account> filtered = accountRepo.findByLoginIdContainingAndEmailAddress("test", "example.com");
}
}