@TransactionalはSpring Bootで機能しません。
Application.Java:
@EnableTransactionManagement(proxyTargetClass=true)
@SpringBootApplication(exclude = {ErrorMvcAutoConfiguration.class})
public class Application {
@Autowired
private EntityManagerFactory entityManagerFactory;
public static void main(String[] args) {
System.out.println("--------------------------- Start Application ---------------------------");
ApplicationContext ctx = SpringApplication.run(Application.class, args);
}
@Bean
public SessionFactory getSessionFactory() {
if (entityManagerFactory.unwrap(SessionFactory.class) == null) {
throw new NullPointerException("factory is not a hibernate factory");
}
return entityManagerFactory.unwrap(SessionFactory.class);
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan(new String[] { "com.buhryn.interviewer.models" });
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(additionalProperties());
return em;
}
@Bean
public DataSource dataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.postgresql.Driver");
dataSource.setUrl("jdbc:postgresql://localhost:5432/interviewer");
dataSource.setUsername("postgres");
dataSource.setPassword("postgres");
return dataSource;
}
@Bean
@Autowired
public HibernateTransactionManager transactionManager(SessionFactory sessionFactory) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(sessionFactory);
return txManager;
}
@Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
return new PersistenceExceptionTranslationPostProcessor();
}
Properties additionalProperties() {
Properties properties = new Properties();
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
properties.setProperty("hibernate.show_sql", "false");
properties.setProperty("hibernate.format_sql", "false");
properties.setProperty("hibernate.hbm2ddl.auto", "create");
properties.setProperty("hibernate.current_session_context_class", "org.hibernate.context.internal.ThreadLocalSessionContext");
return properties;
}
}
CandidateDao.Java
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
@Repository
public class CandidateDao implements ICandidateDao{
@Autowired
SessionFactory sessionFactory;
protected Session getCurrentSession(){
return sessionFactory.getCurrentSession();
}
@Override
@Transactional
public CandidateModel create(CandidateDto candidate) {
CandidateModel candidateModel = new CandidateModel(candidate.getFirstName(), candidate.getLastName(), candidate.getEmail(), candidate.getPhone());
getCurrentSession().save(candidateModel);
return candidateModel;
}
@Override
public CandidateModel show(Long id) {
return new CandidateModel(
"new",
"new",
"new",
"new");
}
@Override
public CandidateModel update(Long id, CandidateDto candidate) {
return new CandidateModel(
"updated",
candidate.getLastName(),
candidate.getEmail(),
candidate.getPhone());
}
@Override
public void delete(Long id) {
}
}
サービスクラス
@Service
public class CandidateService implements ICandidateService{
@Autowired
ICandidateDao candidateDao;
@Override
public CandidateModel create(CandidateDto candidate) {
return candidateDao.create(candidate);
}
@Override
public CandidateModel show(Long id) {
return candidateDao.show(id);
}
@Override
public CandidateModel update(Long id, CandidateDto candidate) {
return candidateDao.update(id, candidate);
}
@Override
public void delete(Long id) {
candidateDao.delete(id);
}
}
Controller.class
@RestController
@RequestMapping(value = "/api/candidates")
public class CandidateController {
@Autowired
ICandidateService candidateService;
@RequestMapping(value="/{id}", method = RequestMethod.GET)
public CandidateModel show(@PathVariable("id") Long id) {
return candidateService.show(id);
}
@RequestMapping(method = RequestMethod.POST)
public CandidateModel create(@Valid @RequestBody CandidateDto candidate, BindingResult result) {
RequestValidator.validate(result);
return candidateService.create(candidate);
}
@RequestMapping(value="/{id}", method = RequestMethod.PUT)
public CandidateModel update(@PathVariable("id") Long id, @Valid @RequestBody CandidateDto candidate, BindingResult result) {
RequestValidator.validate(result);
return candidateService.update(id, candidate);
}
@RequestMapping(value="/{id}", method = RequestMethod.DELETE)
public void delete(@PathVariable("id") Long id) {
candidateService.delete(id);
}
}
DAOシステムでcreateメソッドを呼び出すと、throwexception:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.orm.jpa.JpaSystemException: save is not valid without active transaction; nested exception is org.hibernate.HibernateException: save is not valid without active transaction
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.Java:978)
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.Java:868)
javax.servlet.http.HttpServlet.service(HttpServlet.Java:644)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.Java:842)
javax.servlet.http.HttpServlet.service(HttpServlet.Java:725)
org.Apache.Tomcat.websocket.server.WsFilter.doFilter(WsFilter.Java:52)
org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration$ApplicationContextHeaderFilter.doFilterInternal(EndpointWebMvcAutoConfiguration.Java:291)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.Java:107)
org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.Java:77)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.Java:107)
org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.Java:102)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.Java:107)
org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.Java:85)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.Java:107)
org.springframework.boot.actuate.autoconfigure.MetricFilterAutoConfiguration$MetricsFilter.doFilterInternal(MetricFilterAutoConfiguration.Java:90)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.Java:107)
私のGradleファイル:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.2.3.RELEASE")
}
}
apply plugin: 'Java'
apply plugin: 'idea'
apply plugin: 'spring-boot'
jar {
baseName = 'interviewer'
version = '0.1.0'
}
repositories {
mavenCentral()
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
dependencies {
compile("org.springframework.boot:spring-boot-starter-web")
compile("org.springframework.boot:spring-boot-starter-actuator")
compile("org.codehaus.jackson:jackson-mapper-asl:1.9.13")
compile("com.google.code.gson:gson:2.3.1")
compile("org.springframework.data:spring-data-jpa:1.8.0.RELEASE")
compile("org.hibernate:hibernate-entitymanager:4.3.10.Final")
compile("postgresql:postgresql:9.1-901-1.jdbc4")
compile("org.aspectj:aspectjweaver:1.8.6")
testCompile("org.springframework.boot:spring-boot-starter-test")
}
task wrapper(type: Wrapper) {
gradleVersion = '2.3'
}
Gitリポジトリへのリンク: https://github.com/Yurii-Buhryn/interviewer
最初にSpring Bootを使用してから、Spring Bootを使用して、自動設定を行います。データソース、エンティティマネージャファクトリ、トランザクションマネージャなどを構成します。
次に、間違ったトランザクションマネージャを使用している場合、JPAを使用しているので、JpaTransactionManager
の代わりにHibernateTransactionManager
を使用する必要があります。
次に、hibernate.current_session_context_class
が適切なtx統合を台無しにして削除します。
これらすべてを考慮すると、基本的にApplication
クラスを次のように減らすことができます。
@SpringBootApplication(exclude = {ErrorMvcAutoConfiguration.class})
@EntityScan("com.buhryn.interviewer.models")
public class Application {
public static void main(String[] args) {
System.out.println("--------------------------- Start Application ---------------------------");
ApplicationContext ctx = SpringApplication.run(Application.class, args);
}
@Bean
public SessionFactory sessionFactory(EntityManagerFactory emf) {
if (emf.unwrap(SessionFactory.class) == null) {
throw new NullPointerException("factory is not a hibernate factory");
}
return emf.unwrap(SessionFactory.class);
}
}
次に、以下を含むapplication.properties
にsrc/main/resources
を追加します。
# DataSource configuration
spring.datasource.driver-class-name=org.postgresql.Driver
spring.datasource.username=postgres
spring.datasource.password=postgres
spring.datasource.url=jdbc:postgresql://localhost:5432/interviewer
# General JPA properties
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.show-sql=false
# Hibernate Specific properties
spring.jpa.properties.hibernate.format_sql=false
spring.jpa.hibernate.ddl-auto=create
これにより、データソースとJPAが正しく構成されます。
別のヒントでは、プレーンなhibernate APIを使用する代わりに、SessionFactory
のBeanも削除できるようにJPAを使用します。 EntityManager
の代わりにSessionFactory
を使用するようにdaoを変更するだけです。
@Repository
public class CandidateDao implements ICandidateDao{
@PersistenceContext
private EntityManager em;
@Override
@Transactional
public CandidateModel create(CandidateDto candidate) {
CandidateModel candidateModel = new CandidateModel(candidate.getFirstName(), candidate.getLastName(), candidate.getEmail(), candidate.getPhone());
return em.persist(candidateModel);
}
@Override
public CandidateModel show(Long id) {
return new CandidateModel(
"new",
"new",
"new",
"new");
}
@Override
public CandidateModel update(Long id, CandidateDto candidate) {
return new CandidateModel(
"updated",
candidate.getLastName(),
candidate.getEmail(),
candidate.getPhone());
}
@Override
public void delete(Long id) {
}
}
また、Spring Data JPAをミックスに追加し、DAOを完全に削除し、インターフェイスのみを残したい場合に役立ちます。これで、サービスクラス(IMHOに属する)に移動します。
リポジトリ全体
public interface ICandidateDao extends JpaRepository<CandidateModel, Long> {}
変更されたサービス(必要に応じてトランザクションでもあり、すべてのビジネスロジックはサービス内にあります)。
@Service
@Transactional
public class CandidateService implements ICandidateService{
@Autowired
ICandidateDao candidateDao;
@Override
public CandidateModel create(CandidateDto candidate) {
CandidateModel candidateModel = new CandidateModel(candidate.getFirstName(), candidate.getLastName(), candidate.getEmail(), candidate.getPhone());
return candidateDao.save(candidate);
}
@Override
public CandidateModel show(Long id) {
return candidateDao.findOne(id);
}
@Override
public CandidateModel update(Long id, CandidateDto candidate) {
CandidateModel cm = candidateDao.findOne(id);
// Update values.
return candidateDao.save(cm);
}
@Override
public void delete(Long id) {
candidateDao.delete(id);
}
}
SessionFactory
のBean定義を削除して、Application
をmain
メソッドに減らすこともできます。
@SpringBootApplication(exclude = {ErrorMvcAutoConfiguration.class})
@EntityScan("com.buhryn.interviewer.models")
public class Application {
public static void main(String[] args) {
System.out.println("--------------------------- Start Application ---------------------------");
ApplicationContext ctx = SpringApplication.run(Application.class, args);
}
}
したがって、フレームワークを回避するのではなく、フレームワークを使用することを強くお勧めします。それはあなたの開発者のライブを本当に簡素化するでしょう。
最後に、依存関係からspring-data-jpa
依存関係を削除し、代わりにスターターを使用することをお勧めします。 AspectJについても同じことが言え、そのためにAOPスターターを使用します。また、ジャクソン1はもうサポートされていないため、その依存関係を追加しても何も追加されません
dependencies {
compile("org.springframework.boot:spring-boot-starter-web")
compile("org.springframework.boot:spring-boot-starter-actuator")
compile("org.springframework.boot:spring-boot-starter-data-jpa")
compile("org.springframework.boot:spring-boot-starter-aop")
compile("com.google.code.gson:gson:2.3.1")
compile("org.hibernate:hibernate-entitymanager:4.3.10.Final")
compile("postgresql:postgresql:9.1-901-1.jdbc4")
testCompile("org.springframework.boot:spring-boot-starter-test")
}