[英]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:使用相同的
dogRepository
到select数据库中的现有狗(我手动插入)工作正常:
@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 :编辑:
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. 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./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.java
的transactionManager()
方法中将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 :编辑:
no transaction is in progress
error earlier (that error helped understand what was wrong): the error only appeared after I addedno 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')
在控制台中)。
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
。
public class WebConfig implements WebMvcConfigurer {...}
?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]
public class WebConfig
and public class DbConfig
.public class WebConfig
和public class DbConfig
上重复了注释。 @Configuration
is enough on WebConfig
. @Configuration
在WebConfig
上就足够了。 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.