简体   繁体   English

Hibernate Spring PostgreSQL:没有事务正在进行

[英]Hibernate Spring PostgreSQL: no transaction is in progress Hibernate commands work, JpaRepository save method doesn't save entity in database ignored

I have a simple Dog entity:我有一个简单的Dog实体:

Dog.java :狗.java

@Entity
@Table(name = "dogs")
public class Dog {
    public Dog()  {}

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column
    private String name;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

DogController.java : DogController.java

@RestController
public class DogController {
    @Autowired
    private DogRepository dogRepository;

    @RequestMapping("/api/dogs/insert/default")
    @Transactional
    public String insertDefault()  {
        Dog dog = new Dog();
        dog.setName("Alpha dog");
        dogRepository.save(dog);
        return "dog inserted OK";
    }
}

DogRepository :狗库

@Repository
public interface DogRepository extends JpaRepository<Dog, Long> {
}

However when I go to /api/dogs/insert/default it simply displays dog inserted OK without changing a database or even attempting to change it (no queries are printed to the console).但是,当我将 go 更改为/api/dogs/insert/default时,它只显示dog inserted OK而不更改数据库甚至尝试更改它(没有查询打印到控制台)。

What could be the problem here?这里可能是什么问题? In debugger I can see that dogRepository is not null.在调试器中,我可以看到dogRepository不是 null。

My WebMvcConfigurer class looks like this:我的WebMvcConfigurer class 看起来像这样:

@EnableWebMvc
@Configuration
@EnableJpaRepositories("testproject")
@EnableTransactionManagement
@ComponentScan("testproject")
public class WebConfig implements WebMvcConfigurer {...}

I tried adding the @EnableTransactionManagement to it, but it didn't work我尝试将@EnableTransactionManagement添加到它,但它没有用

EDIT :编辑

I don't have an application.properties file.我没有application.properties文件。 All properties are configured using Java classes.所有属性均使用 Java 类进行配置。

DbConfig.java : DbConfig.java

@Configuration
 @EnableTransactionManagement
@EnableJpaRepositories(basePackages = "testproject", entityManagerFactoryRef = "entityManagerFactory")
 public class DbConfig {
    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
        entityManagerFactoryBean.setDataSource(getDatasource());
        entityManagerFactoryBean.setPersistenceProviderClass(HibernatePersistenceProvider.class);

        entityManagerFactoryBean.setJpaProperties(getHibernateProperties());

        entityManagerFactoryBean.setPackagesToScan("testproject");

        return entityManagerFactoryBean;
    }


    @Bean
    public DataSource getDatasource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("org.postgresql.Driver");
        dataSource.setUrl("jdbc:postgresql://127.0.0.1:5432/testproject");
        dataSource.setUsername("postgres");
        dataSource.setPassword("bigfalcon");
        return dataSource;
    }

    @Bean
    public SessionFactory getSessionFactory() throws IOException {
        LocalSessionFactoryBean sessionFactoryBean = new LocalSessionFactoryBean();
        sessionFactoryBean.setPackagesToScan("testproject");
        //getHibernateProperties method is a private method

        sessionFactoryBean.setHibernateProperties(getHibernateProperties());
        sessionFactoryBean.setDataSource(getDatasource());
        sessionFactoryBean.afterPropertiesSet();

        return sessionFactoryBean.getObject();
    }

    @Bean(name = "transactionManager")
    public HibernateTransactionManager getTransactionManager() throws IOException {
        HibernateTransactionManager transactionManager = new HibernateTransactionManager();
        transactionManager.setSessionFactory(getSessionFactory());
        return transactionManager;
    }


    @Bean
    private static Properties getHibernateProperties() {
        Properties hibernateProperties = new Properties();  //PostgreSQLDialect
        //hibernateProperties.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
        hibernateProperties.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
        hibernateProperties.put("hibernate.show_sql", true);
    //    hibernateProperties.put("spring.jpa.hibernate.ddl-auto", "create");

        hibernateProperties.put( "hibernate.hbm2ddl.auto", "create-drop");
         /*hibernateProperties.setProperty(
                "hibernate.dialect", "org.hibernate.dialect.H2Dialect");
*/
        System.out.println();
        // other properties
        return hibernateProperties;
    }
}

EDIT :编辑

Using the same dogRepository to select existing dogs from the database (which I manually inserted) works fine:使用相同的dogRepositoryselect数据库中的现有狗(我手动插入)工作正常:

 @RequestMapping("/api/dogs/view/all")
    public Iterable<Dog> viewDogs()  {
        Iterable<Dog> dogs = dogRepository.findAll(); //non-null size, returns a JSON with dogs
        return dogs;
    }

EDIT : Maven dependencies:编辑:Maven 依赖项:

<dependencies>

    <!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-core -->
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-core</artifactId>
        <version>5.3.1.RELEASE</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.2.5.RELEASE</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>5.2.5.RELEASE</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-core -->
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-core</artifactId>
        <version>5.3.1.RELEASE</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-config -->
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-config</artifactId>
        <version>5.3.1.RELEASE</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-web -->
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-web</artifactId>
        <version>5.3.1.RELEASE</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-jpa -->
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-jpa</artifactId>
        <version>2.2.6.RELEASE</version>
    </dependency>


    <!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>5.2.5.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.thymeleaf</groupId>
        <artifactId>thymeleaf</artifactId>
        <version>3.0.7.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.thymeleaf</groupId>
        <artifactId>thymeleaf-spring4</artifactId>
        <version>3.0.7.RELEASE</version>
    </dependency>


    <dependency>
        <groupId>org.apache.opennlp</groupId>
        <artifactId>opennlp-tools</artifactId>
        <version>1.9.1</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/net.dv8tion/JDA -->
    <dependency>
        <groupId>net.dv8tion</groupId>
        <artifactId>JDA</artifactId>
        <version>4.0.0_46</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-core -->
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-core</artifactId>
        <version>5.4.4.Final</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/com.h2database/h2 -->
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <version>1.4.197</version>
        <scope>test</scope>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework/spring-orm -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-orm</artifactId>
        <version>5.2.5.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <version>9.4-1203-jdbc4</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>4.0.1</version>
        <scope>provided</scope>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.springframework.security.oauth/spring-security-oauth2 -->
    <dependency>
        <groupId>org.springframework.security.oauth</groupId>
        <artifactId>spring-security-oauth2</artifactId>
        <version>2.4.1.RELEASE</version>
    </dependency>

    <!--is needed for jwt-->
    <!-- https://mvnrepository.com/artifact/javax.xml/jaxb-api -->
    <dependency>
        <groupId>javax.xml</groupId>
        <artifactId>jaxb-api</artifactId>
        <version>2.1</version>
    </dependency>


    <!-- https://mvnrepository.com/artifact/com.sun.xml.bind/jaxb-impl -->
    <dependency>
        <groupId>com.sun.xml.bind</groupId>
        <artifactId>jaxb-impl</artifactId>
        <version>3.0.0-M4</version>
    </dependency>


    <!-- https://mvnrepository.com/artifact/com.sun.xml.bind/jaxb-core -->
    <dependency>
        <groupId>com.sun.xml.bind</groupId>
        <artifactId>jaxb-core</artifactId>
        <version>3.0.0-M4</version>
    </dependency>


    <!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-jwt -->
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-jwt</artifactId>
        <version>1.1.0.RELEASE</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt -->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.1</version>
    </dependency>


    <!-- https://mvnrepository.com/artifact/com.jakewharton.fliptables/fliptables -->
    <dependency>
        <groupId>com.jakewharton.fliptables</groupId>
        <artifactId>fliptables</artifactId>
        <version>1.0.2</version>
    </dependency>

    <dependency>
        <groupId>com.squareup.okhttp3</groupId>
        <artifactId>okhttp</artifactId>
        <version>3.4.2</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.json/json -->
    <dependency>
        <groupId>org.json</groupId>
        <artifactId>json</artifactId>
        <version>20190722</version>
    </dependency>



    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>

    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.9.8</version>
    </dependency>
</dependencies>

EDIT :编辑

I also tried to get it to update the entity:我还试图让它更新实体:

@RequestMapping("/api/dogs/update/{id}")
public Iterable<Dog> updateDog(@PathVariable("id") long id)  {
    Optional<Dog> dog = dogRepository.findById(id);
    Dog dogValue = dog.get();
    dogValue.setName("New name");
    dogRepository.save(dogValue);
    return dogRepository.findAll();
}

which however doesn't work: the JSON returned to the browser contains updated name ( New name ), but doesn't update the database itself.然而这不起作用:返回给浏览器的 JSON 包含更新的名称( New name ),但不更新数据库本身。

EDIT :编辑

I managed to get it to work with manual Hibernate commands, see the added methods insertDog(Dog dog) and updateDog(Dog dog) : I take the Session object from autowired DbConfig object and use session.save(dog) , session.flush() to update the database. I managed to get it to work with manual Hibernate commands, see the added methods insertDog(Dog dog) and updateDog(Dog dog) : I take the Session object from autowired DbConfig object and use session.save(dog) , session.flush()来更新数据库。 With the following controller code both /api/dogs/insert/default and /api/dogs/update/3/Buddy work fine.使用以下 controller 代码/api/dogs/insert/default/api/dogs/update/3/Buddy都可以正常工作。

So the problem seems to be related to JpaRepository specifically, seeing as if I forego using it and write the Hibernate code myself, it works fine:所以这个问题似乎与JpaRepository具体有关,好像我放弃使用它并自己编写 Hibernate 代码,它工作正常:

DogController.java : DogController.java

@CrossOrigin
@RestController
@Transactional(  readOnly = false)
public class DogController {
    @Autowired
    private DogRepository dogRepository;

    @Autowired
    private DbConfig dbConfig;

    @RequestMapping("/api/dogs/insert/default")
    @Transactional(readOnly=false)
    public String insertDefault() throws Exception {
        Dog dog = new Dog();
        dog.setName("Alpha dog");
        //dogRepository.save(dog);
        insertDog(dog);
        return "dog inserted OK";
    }

    @RequestMapping("/api/dogs/view/all")
    public Iterable<Dog> viewDogs()  {
        Iterable<Dog> dogs = dogRepository.findAll();
        return dogs;
    }

    @RequestMapping("/api/dogs/update/{id}/{name}")
    public Iterable<Dog> updateDog(@PathVariable("id") long id,
                                @PathVariable("name") String name)  throws Exception  {
        Optional<Dog> dog = dogRepository.findById(id);
        Dog dogValue = dog.get();
        dogValue.setName(name);
        //dogRepository.save(dogValue);
        Session session = dbConfig.getSessionFactory().openSession();
        updateDog(dogValue);
        return dogRepository.findAll();
    }

    public void updateDog(Dog dog) throws Exception  {
        Session session = dbConfig.getSessionFactory().openSession();
        session.beginTransaction();

        session.merge(dog);
        session.flush();
        session.getTransaction().commit();
        session.clear();
        session.close();
    }


    public boolean insertDog(Dog dog)  throws Exception  {
        boolean result = false;

        Session session = dbConfig.getSessionFactory().openSession();
        session.beginTransaction();
        try {
            session.save(dog);
            session.flush();
            session.getTransaction().commit();

            result = true;
        }
        catch (Exception e)  {
            result = false;
        }
        finally  {
            session.clear();
            session.close();
        }

        return result;
    }
}

EDIT :编辑

I wonder if something to do with generating ids of the Dog entity could be the problem.我想知道是否与生成Dog实体的 id 有关可能是问题所在。 I use PostgreSQL and in the logs I see我使用 PostgreSQL 并在我看到的日志中

WARN [RMI TCP Connection(2)-127.0.0.1] org.hibernate.engine.jdbc.spi.SqlExceptionHelper$StandardWarningHandler.logWarning sequence "hibernate_sequence" does not exist, skipping

And then when I access /api/dogs/insert/default in console然后当我在控制台中访问/api/dogs/insert/default

Hibernate: select nextval ('hibernate_sequence')

gets printed.被打印出来。

I tried changing GenerationStrategy but it didn't help.我尝试更改GenerationStrategy但没有帮助。 I also tried adding我也尝试添加

hibernateProperties.put("hibernate.connection.driver_class", "org.postgresql.Driver");

to getHibernateProperties() method in DbConfig.java but it didn't seem to help either.DbConfig.java中的getHibernateProperties()方法,但它似乎也没有帮助。

EDIT :编辑

I also tried changing HibernateTransactionManager to JpaTransactionManager in transactionManager() method in DbConfig.java :我还尝试在DbConfig.javatransactionManager()方法中将HibernateTransactionManager更改为JpaTransactionManager

 @Bean(name = "transactionManager")
     public JpaTransactionManager transactionManager() throws IOException {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        //transactionManager.setSessionFactory(getSessionFactory());
        return transactionManager;
    }

but that resulted the但这导致了

NoUniqueBeanDefinitionException: No qualifying bean of type 'javax.persistence.EntityManagerFactory' available: more than one 'primary' bean found among candidates: [entityManagerFactory, getSessionFactory]

error on server startup服务器启动时出错

I also figured that maybe JpaRepository can't generate the id, so I tried making it manual:我还想也许JpaRepository不能生成 id,所以我试着让它手动:

@Id
@Column(name = "dog_id", unique = true, nullable = false)
private Long id;

and in DogController.java :DogController.java中:

@RequestMapping("/api/dogs/insert/default")
    @Transactional(readOnly=false, propagation =  Propagation.REQUIRED)
    public String insertDefault() throws Exception {
        Dog dog = new Dog();
        Random random = new Random();
        dog.setId((long) random.nextInt(1000_000_000));
        dog.setName("Alpha dog");
        dogRepository.save(dog);
        dogRepository.flush();
        //insertDog(dog);
        return "dog inserted OK";
    }

however it resulted in error:但是它导致了错误:

org.springframework.web.util.NestedServletException: 
Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: 
no transaction is in progress; nested exception is javax.persistence.TransactionRequiredException: no transaction is in progress

EDIT :编辑

Also I figured out why there was no no transaction is in progress error earlier (that error helped understand what was wrong): the error only appeared after I added我还弄清楚了为什么之前no transaction is in progress错误(该错误有助于理解出了什么问题):该错误仅在我添加后才出现

dogRepository.flush();

to DogController.java .DogController.java

If I remove dogRepository.flush() the dogRepository.save(dog) method still wouldn't work, but no visible error would appear (other than Hibernate: select nextval ('hibernate_sequence') in console).如果我删除dogRepository.flush()dogRepository.save(dog)方法仍然不起作用,但不会出现任何可见错误(除了Hibernate: select nextval ('hibernate_sequence')在控制台中)。

I managed to make JpaRepository insert on /api/dogs/insert/default work!我设法使JpaRepository/api/dogs/insert/default上插入工作!

What it took: adding它需要什么:添加

hibernateProperties.put("hibernate.allow_update_outside_transaction",  true);

to getHibernateProperties() method of DbConfig.java class. DbConfig.java class 的 HibernateProperties getHibernateProperties()方法。

However I'm not sure that's a correct way to handle it?但是我不确定这是处理它的正确方法吗? And if there are better methods of dealing with that error?如果有更好的方法来处理这个错误? And why that error only appeared when I use JpaRepository for inserting/updating entities, while if I use raw Hibernate commands ( session.save() ) the error didn't appear and updates worked fine?以及为什么该错误仅在我使用JpaRepository插入/更新实体时出现,而如果我使用原始 Hibernate 命令( session.save() )错误没有出现并且更新工作正常?

Transactional is read-only by default.默认情况下,事务是只读的。 Write queries fails silently.写入查询以静默方式失败。

Set your annotation as @Transactional(readOnly=false)将您的注释设置为@Transactional(readOnly=false)

Your configuration is almost OK, i changed a couple of things and it works on my Spring Boot v2.3.1.RELEASE .你的配置几乎没问题,我改变了一些东西,它适用于我的Spring Boot v2.3.1.RELEASE

  • Do you need this class public class WebConfig implements WebMvcConfigurer {...} ?你需要这个 class public class WebConfig implements WebMvcConfigurer {...}吗? If yes consider annotating either public LocalContainerEntityManagerFactoryBean entityManagerFactory() or this public SessionFactory getSessionFactory() throws IOException { with @Primary .如果是,请考虑注释public LocalContainerEntityManagerFactoryBean entityManagerFactory()或这个public SessionFactory getSessionFactory() throws IOException { with @Primary Else you will get this:否则你会得到这个:

Parameter 0 of method openEntityManagerInViewInterceptorConfigurer in org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration$JpaWebConfiguration required a single bean, but 2 were found: org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration$JpaWebConfiguration 中的方法 openEntityManagerInViewInterceptorConfigurer 的参数 0 需要单个 bean,但找到了 2 个:

  • entityManagerFactory: defined by method 'entityManagerFactory' in class path resource [.../DbConfig.class] entityManagerFactory:由 class 路径资源中的方法 'entityManagerFactory' 定义 [.../DbConfig.class]
  • getSessionFactory: defined by method 'getSessionFactory' in class path resource [.../DbConfig.class] getSessionFactory:由 class 路径资源中的方法“getSessionFactory”定义 [.../DbConfig.class]
  • You have repeated annotations on public class WebConfig and public class DbConfig .您在public class WebConfigpublic class DbConfig上重复了注释。 @Configuration is enough on WebConfig . @ConfigurationWebConfig上就足够了。

Otherwise, it looks fine, just make sure that everywhere you define testproject as values is correct and that your postgresql is accessible:否则,它看起来很好,只需确保您将testproject testproject为值的任何地方都是正确的,并且您的postgresql是可访问的:

Spring Boot v2.3.1.RELEASE:
Completed initialization in 5 ms
Hibernate: insert into dogs (name) values (?)

PostgreSQL:
postgres=# SELECT version();
                          version
------------------------------------------------------------
 PostgreSQL 12.3, compiled by Visual C++ build 1914, 64-bit
(1 row)


postgres=#
postgres=# select * from dogs;
 id |   name
----+-----------
  1 | Alpha dog
(1 row)


postgres=#

PS : I tested with an empty WebConfig class, like so: PS :我用一个WebConfig class 进行了测试,如下所示:

@Configuration
public class WebConfig implements WebMvcConfigurer {

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM