@Configuration
@EnableJpaRepositories(
basePackages = "com.maxxyoung.**.repository",
entityManagerFactoryRef = "entityManagerFactory"
)
@MapperScan(
basePackages = "com.maxxyoung.**.dao",
sqlSessionFactoryRef = "sqlSessionFactory"
)
public class DatabaseConfig {
/**
* hikari config
*/
@Bean(name= "hikariConfig")
@ConfigurationProperties(prefix = "spring.datasource.hikari")
public HikariConfig hikariConfig() {
return new HikariConfig();
}
/**
* datasource
*/
@Bean(name= "dataSource")
public HikariDataSource dataSource(@Qualifier("hikariConfig") HikariConfig hikariConfig) {
return new HikariDataSource(hikariConfig);
}
@Bean(name= "jpaDataSource")
public HikariDataSource jpaDataSource(@Qualifier("hikariConfig") HikariConfig hikariConfig) {
return new HikariDataSource(hikariConfig);
}
/**
* sessionfactory
*/
@Bean(name= "sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
sessionFactoryBean.setDataSource(dataSource);
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
sessionFactoryBean.setMapperLocations(resolver.getResources("classpath:/mybatis/sql/**/*.xml")); //mapper path
sessionFactoryBean.setTypeAliasesPackage("com.maxxyoung.domain, com.maxxyoung.dto");
sessionFactoryBean.setTypeHandlersPackage("com.maxxyoung.typehandler");
Objects.requireNonNull(sessionFactoryBean.getObject()).getConfiguration().setMapUnderscoreToCamelCase(true); //camelCase
return sessionFactoryBean.getObject();
}
/**
* sqlsession
*/
@Bean(name= "sqlSessionTemplate")
public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
/**
* jpa entityManagerFactory
*/
@Bean(name = "entityManagerFactory")
public EntityManagerFactory entityManagerFactory(@Qualifier("jpaDataSource") DataSource dataSource) {
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setPackagesToScan("kr.influencercard.**.jpa.entity");
factory.setDataSource(dataSource);
factory.setPersistenceUnitName("MySQL");
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setShowSql(true);
factory.setJpaVendorAdapter(vendorAdapter);
Map<String, Object> properties = new HashMap<>();
properties.put("hibernate.hbm2ddl.auto", "none");
properties.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
properties.put("hibernate.format_sql", true);
properties.put("hibernate.show_sql", false); // sql은 log4j로 출력 org.hibernate.SQL=DEBUG
properties.put("hibernate.globally_quoted_identifiers", true); // 예약어 컬럼명 사용 허용
factory.setJpaPropertyMap(properties);
factory.afterPropertiesSet();
return factory.getObject();
}
/**
* transaction manager
*/
@Bean(name= "txManager")
public PlatformTransactionManager txManager(@Qualifier("dataSource") DataSource dataSource) {
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(dataSource);
dataSourceTransactionManager.setNestedTransactionAllowed(true); // nested
return dataSourceTransactionManager;
}
@Bean(name= "jpaTxManager")
public PlatformTransactionManager jpaTxManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
jpaTransactionManager.setEntityManagerFactory(entityManagerFactory);
return jpaTransactionManager;
}
처음에는 다음과 같이 설정을 진행했다. 설정을 통해 프로퍼티에서 hikari 정보
를 가져오고, 가져온 정보로 jpa, jdbc 트랜잭션을 각각 만들어준다.
설정을 하는 도중에 properties spring.datasource.hikari
데이터를 가져와 사용하는데 이미 dataSource 데이터를 가지고 있는 spring.datasource
와의 차이점이 무엇인지 궁금했다.
spring.datasource.hikari, spring.datasource 설정 차이
- 자동 설정
- spring.datasource.url이 모든 Datasource의 url이 된다.
- 수동 설정 (Java Config)
- spring.datasource.jdbc-url로 해야 HikariCP가 인식한다.
- spring.datasource.hikari 설정을 이용할 경우 자동, 수동 설정에 관계 없이 사용가능
참고 향로님 블로그
(실제로 java config로 설정을 하는데 있어서 spring.datasource.url만가지고는 jdbc-url을 찾는다는 오류를 뱉었다.)
다음과 같이 트랜잭션 2개를 설정할 경우 문제가 있다
@Transactional(value="사용할 트랜잭션 이름")
써주어한다.(트랜잭션 2개 중 어떤 트랜잭션 사용할지)@Configuration
@EnableJpaRepositories(basePackages = "com.maxxyoung.**.repository")
@MapperScan("com.maxxyoung.dao, com.maxxyoung.inflma.dao")
public class DatabaseConfig {
JpaTransactionManager transactionManager() {
return new JpaTransactionManager();
}
}
JPA transaction manager
를 기본 트랜잭션 매니저로 등록해주면 된다!jpa transaction manager
는 data source
에 직접 접근이 가능하기 때문에 mybatis와 동시에 사용이 가능
한가지 주의해야할 점은 JPA 트랜잭션 매니저와 JDBC Connection을 위한 DataSource는 동일한 DataSource 객체를 바라보고 있어야 하나의 트랜잭션으로 제대로 묶임
참고 : 일반 JDBC 코드에 대해 DataSource의 연결을 등록하려면 Jpa 트랜잭션 매니저 인스턴스가 DataSource (setDataSource (javax.sql.DataSource))를 인식해야합니다. 주어진 DataSource는 JPA 트랜잭션 매니저의 EntityManagerFactory에서 사용하는 것과 분명히 일치해야합니다. JPA 트랜잭션 매니저는 EntityManagerFactory의 연결 팩토리로 사용되는 DataSource를 자동 감지하므로 일반적으로 "dataSource"속성을 명시적으로 지정할 필요가 없습니다.
However, if we're using a Spring Boot project and have a spring-data-* or spring-tx dependencies on the classpath, then transaction management will be enabled by default.
스프링부트의 경우 spring-data-*
에 따라 트랜잭션 매니저가 자동으로 등록된다. JPA를 사용한다면 JpaTransactionManager
가 자동으로 등록된다.
아마 이 부분이지 않을까 싶다.
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(JpaProperties.class)
@Import(DataSourceInitializedPublisher.Registrar.class)
public abstract class JpaBaseConfiguration implements BeanFactoryAware {
private final DataSource dataSource;
private final JpaProperties properties;
private final JtaTransactionManager jtaTransactionManager;
private ConfigurableListableBeanFactory beanFactory;
@Bean
@ConditionalOnMissingBean
public PlatformTransactionManager transactionManager(
ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManagerCustomizers.ifAvailable((customizers) -> customizers.customize(transactionManager));
return transactionManager;
}
결론! 스프링부트에서 아무설정 안해도 기본으로 jpa 트랜잭션으로 등록된다!