Spring Data CRUD Repository: save方法不起作用。

5

这与Spring Boot无关。

我的英语可能不太好。

使用下面的配置来使用Spring Data尝试执行DML请求。

确切地说,我正在使用 CrudRepository#save 方法。

然而,当我执行Spring的CrudRepository#save方法时,我得到了以下结果:

  1. 只有通过 hibernate.show_sql 特性记录的选择日志。
  2. 没有"insert"或"update"语句被执行,因为没有日志记录。
  3. 数据库中根本没有任何变化。

====================================================

不确定,但这似乎是一个事务问题。

看起来在那个点上没有事务,

所以在事务之外,CRUD Repos 无法执行DML请求,包括 CrudRepository#save

也许配置有问题?请查看并随时要求提供任何其他信息。

更新: 下面这个不良实践的解决方法帮助我执行了"Update"语句。

//(autowired, shared entity manager)
entityManager.joinTransaction();
repository.save(user);

然而这仍然是一种不良的做法。在这种情况下,Spring的目的失去了意义。 无论如何,我需要使用基于声明的代码事务管理。 问题仍然存在: 我的配置出了什么问题?@Transactional注释仍然不起作用。
用户域实体:
@Data
@EqualsAndHashCode(callSuper = true)
@NoArgsConstructor
@Entity
@Table(name = "users")
public class User
{
    @Id
    @Column(name = "id_pk", length = 11)
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int idPk;

    @Column(name = "user_id", length = 25, nullable = false, unique = true)
    private String userId;

    @Column(name = "email_addr", length = 120)
    private String email;
}

领域特定的Spring Data CRUD存储库声明:

public interface UserRepository extends CrudRepository<User, Integer> {
  //nothing specific
}

Spring(无Boot)基于代码的配置:

@EnableJpaRepositories(basePackages = "***",
        transactionManagerRef = "jpaTransactionManager")
@EnableTransactionManagement
public class DataConfig
{
    @Bean
    public EntityManagerFactory entityManagerFactory()
    {
        LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
        factory.setDataSource(dataSource());
        factory.setPackagesToScan(DOMAIN_ENTITY_SCAN_PACKAGE);
        factory.setJpaVendorAdapter(getVendorAdapter());
        factory.afterPropertiesSet();
        return factory.getObject();
    }

    private HibernateJpaVendorAdapter getVendorAdapter()
    {
        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        vendorAdapter.setShowSql(Boolean.TRUE);
        return vendorAdapter;
    }

    @Bean
    public JpaTransactionManager jpaTransactionManager()
    {
        JpaTransactionManager txManager = new JpaTransactionManager();
        txManager.setEntityManagerFactory(entityManagerFactory());
        txManager.afterPropertiesSet();
        return txManager;
    }
}

将id更改为非原始类型,以便它可以接受空值,否则在插入时它将始终是PK 0。 - JLazar0
@JLazar0,感谢您的建议,但是这一点与我上述描述的问题无关。 - user11814208
2个回答

2

最终,我找到了解决方案。

由于我使用的是Spring而不是它的Boot部分, 我必须配置自定义的WebApplicationInitializer以让Spring管理应用程序入口点:

public class MainWebAppInitializer implements WebApplicationInitializer
{
    @Override
    public void onStartup(final ServletContext sc)
    {
        AnnotationConfigWebApplicationContext root = new AnnotationConfigWebApplicationContext();
        root.register(WebAppConfiguration.class, DataConfig.class);
        sc.addListener(new ContextLoaderListener(root));
        
        ...other not related code ommited
    }
}

因为我使用了AnnotationConfigWebApplicationContext#register注册了两个配置类(WebAppConfiguration.class, DataConfig.class), 所以我认为使用@Configuration来注释配置是冗余的。
但是我在那时错了。
为了正确地注册TransactionManager,您应该使用@Configuration来注释您的Jpa配置类。
因此,我已经成功地用@Configuration来注释我的配置类,并解决了我的问题。
现在,Spring CRUD存储库能够运行DML查询到数据库(通过#save方法的帮助)。确切地说:现在存储库能够打开自己的事务,并根据这些事务运行所需的查询。

1
对我来说没问题!我在测试环境中遇到了这个问题。我创建了一个包含所有bean(如DataSource、EntityManagerFactory、PlatformTransactionManager)的配置文件。然后我将此配置添加到 '@ContextConfiguration' 中。但是保存操作无法正常工作。所以我使用 '@Configuration' 标记了配置,现在它像魔法一样运行良好。非常感谢! - Alex

0

我不明白这个,你是如何初始化数据源属性的?我在你给出的代码中没有看到。

factory.setDataSource(dataSource());

你应该像下面这样设计你的配置类。使用两个冒号:
entityManagerFactoryRef="entityManagerFactory",
transactionManagerRef="jpaTransactionManager"

从yaml或properties文件中读取hibernate属性。

并设置您的数据源

配置类:DataConfig

/**
 * @author Som
 *
 */

@Configuration
@EnableJpaRepositories(basePackages="package-name",
                       entityManagerFactoryRef="entityManagerFactory",
                       transactionManagerRef="jpaTransactionManager")
@EnableTransactionManagement
@PropertySource(value = { "classpath:application.yml" })
public class DataConfig {
    
    @Autowired
    private Environment environment;
    
    @Value("${datasource.myapp.maxPoolSize:10}")
    private int maxPoolSize;
    
    
    /**
     * Populate DataSourceProperties object directly from application.yml 
     *       *
     */
    
    @Bean
    @Primary
    @ConfigurationProperties(prefix = "datasource.myapp")
    public DataSourceProperties dataSourceProperties(){
        return new DataSourceProperties();
    }
    
    
    /**
     * Configure HikariCP pooled DataSource.
     * 
     */
    @Bean
    public DataSource dataSource() {
        DataSourceProperties dataSourceProperties = dataSourceProperties();
        HikariDataSource dataSource = (HikariDataSource) DataSourceBuilder
                .create(dataSourceProperties.getClassLoader())
                .driverClassName(dataSourceProperties.getDriverClassName())
                .url(dataSourceProperties.getUrl())
                .username(dataSourceProperties.getUsername())
                .password(dataSourceProperties.getPassword())
                .type(HikariDataSource.class)
                .build();
        
        dataSource.setMaximumPoolSize(maxPoolSize);
        return dataSource;
    }
    
    
    /**
     * Entity Manager Factory setup.
     * 
     */
    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() throws NamingException {
        LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
        factoryBean.setDataSource(dataSource());
        factoryBean.setPackagesToScan(new String[] { "package-name" });
        factoryBean.setJpaVendorAdapter(jpaVendorAdapter());
        factoryBean.setJpaProperties(jpaProperties());
        return factoryBean;
    }
    
    
    
    /**
     * Provider specific adapter.
     * 
     */
    @Bean
    public JpaVendorAdapter jpaVendorAdapter() {
        HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
        return hibernateJpaVendorAdapter;
    }
 
    /**
     * Hibernate properties.
     * 
     */
    private Properties jpaProperties() {
        Properties properties = new Properties();
        properties.put("hibernate.dialect", environment.getRequiredProperty("datasource.myapp.hibernate.dialect"));
        properties.put("hibernate.hbm2ddl.auto", environment.getRequiredProperty("datasource.myapp.hibernate.hbm2ddl.method"));
        properties.put("hibernate.show_sql", environment.getRequiredProperty("datasource.myapp.hibernate.show_sql"));
        properties.put("hibernate.format_sql", environment.getRequiredProperty("datasource.myapp.hibernate.format_sql"));
        if(StringUtils.isNotEmpty(environment.getRequiredProperty("datasource.myapp.defaultSchema"))){
            properties.put("hibernate.default_schema", environment.getRequiredProperty("datasource.myapp.defaultSchema"));
        }
        return properties;
    }
 
    @Bean
    @Autowired
    public PlatformTransactionManager jpaTransactionManager(EntityManagerFactory emf) {
        JpaTransactionManager txManager = new JpaTransactionManager();
        txManager.setEntityManagerFactory(emf);
        return txManager;
    }

}

application.yaml

server:
  port: 8081
  servlet:
    context-path: /CRUDApp
---
spring:
  profiles: local,default
datasource:
  myapp:
    url: jdbc:h2:~/test
    username: SA
    password:
    driverClassName: org.h2.Driver
    defaultSchema:
    maxPoolSize: 10
    hibernate:
      hbm2ddl.method: create-drop
      show_sql: true
      format_sql: true
      dialect: org.hibernate.dialect.H2Dialect
---

很遗憾,那并没有帮助。 - user11814208
1
有趣的是,Transactional注解似乎正在起作用。为什么呢?因为如果我不在方法/类上使用Transactional,我将无法加入事务。因此,它看起来像是事务上下文或类似的东西之间的区别。虽然不确定。 - user11814208
嗯,好的...也许你有一些日志吗?不确定,但这是我在我的情况下实现的方式。我唯一的区别是我使用JPA repo而你正在使用CRUD repo..使用JPA repo只是具有一些额外的功能,如刷新持久性上下文。还告诉我一件事,你是将#transaction注释用作类头还是方法头。 - Som
只有hibernate.show_sql查询被记录,其他日志我故意关闭以避免误导。我特别在方法上使用了Transactional注释。我尝试了两种方式-但都没有成功。我不认为这是关键。感谢您的尝试。 - user11814208
让我们在聊天中继续这个讨论 - user11814208
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接