Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/386.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/jpa/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Spring数据JPA更新@Query未更新?_Java_Jpa_Spring Data - Fatal编程技术网

Java Spring数据JPA更新@Query未更新?

Java Spring数据JPA更新@Query未更新?,java,jpa,spring-data,Java,Jpa,Spring Data,我有一个更新查询: @Modifying @Transactional @Query("UPDATE Admin SET firstname = :firstname, lastname = :lastname, login = :login, superAdmin = :superAdmin, preferenceAdmin = :preferenceAdmin, address = :address, zipCode = :zipCode, city = :city, country =

我有一个更新查询:

@Modifying
@Transactional
@Query("UPDATE Admin SET firstname = :firstname, lastname = :lastname, login = :login, superAdmin = :superAdmin, preferenceAdmin = :preferenceAdmin, address =  :address, zipCode = :zipCode, city = :city, country = :country, email = :email, profile = :profile, postLoginUrl = :postLoginUrl WHERE id = :id")
public void update(@Param("firstname") String firstname, @Param("lastname") String lastname, @Param("login") String login, @Param("superAdmin") boolean superAdmin, @Param("preferenceAdmin") boolean preferenceAdmin, @Param("address") String address, @Param("zipCode") String zipCode, @Param("city") String city, @Param("country") String country, @Param("email") String email, @Param("profile") String profile, @Param("postLoginUrl") String postLoginUrl, @Param("id") Long id);
我试图在集成测试中使用它:

adminRepository.update("Toto", "LeHeros", admin0.getLogin(), admin0.getSuperAdmin(), admin0.getPreferenceAdmin(), admin0.getAddress(), admin0.getZipCode(), admin0.getCity(), admin0.getCountry(), admin0.getEmail(), admin0.getProfile(), admin0.getPostLoginUrl(), admin0.getId());
Admin loadedAdmin = adminRepository.findOne(admin0.getId());
assertEquals("Toto", loadedAdmin.getFirstname());
assertEquals("LeHeros", loadedAdmin.getLastname());
但是字段没有更新并保留其初始值,因此测试失败

我尝试在findOne查询之前添加刷新:

adminRepository.flush();
但失败的断言仍然相同

我可以在日志中看到update sql语句:

update admin set firstname='Toto', lastname='LeHeros', login='stephane', super_admin=0, preference_admin=0,
address=NULL, zip_code=NULL, city=NULL, country=NULL, email='stephane@thalasoft.com', profile=NULL,
post_login_url=NULL where id=2839
但是日志中没有显示与查找器相关的sql:

Admin loadedAdmin = adminRepository.findOne(admin0.getId());
The finder sql statement is not making its way to the database.
是否由于某些缓存原因而忽略了它

如果我随后向findByEmail和findByLogin查找程序添加调用,如中所示:

adminRepository.update("Toto", "LeHeros", "qwerty", admin0.getSuperAdmin(), admin0.getPreferenceAdmin(), admin0.getAddress(), admin0.getZipCode(), admin0.getCity(), admin0.getCountry(), admin0.getEmail(), admin0.getProfile(), admin0.getPostLoginUrl(), admin0.getId());
Admin loadedAdmin = adminRepository.findOne(admin0.getId());
Admin myadmin = adminRepository.findByEmail(admin0.getEmail());
Admin anadmin = adminRepository.findByLogin("qwerty");
assertEquals("Toto", anadmin.getFirstname());
assertEquals("Toto", myadmin.getFirstname());
assertEquals("Toto", loadedAdmin.getFirstname());
assertEquals("LeHeros", loadedAdmin.getLastname());
然后我可以在日志中看到正在生成的sql语句:

但是断言:

assertEquals("Toto", myadmin.getFirstname());
即使跟踪显示检索到相同的域对象,仍然会失败:

TRACE [BasicExtractor] found [1037] as column [id14_]
另一件让我对另一个查找程序感到困惑的事情是,它显示了一个limit 2子句,尽管它应该只返回一个Admin对象

我认为在返回一个域对象时总是有一个限制1。这是对Spring数据的错误假设吗

粘贴到MySQL客户端时,控制台日志中显示的sql语句,逻辑工作正常:

mysql> insert into admin (version, address, city, country, email, firstname, lastname, login, password, 
-> password_salt, post_login_url, preference_admin, profile, super_admin, zip_code) values (0,
-> NULL, NULL, NULL, 'zemail@thalasoft.com039', 'zfirstname039', 'zlastname039', 'zlogin039',
-> 'zpassword039', '', NULL, 0, NULL, 1, NULL);
Query OK, 1 row affected (0.07 sec)

mysql> select * from admin;
+------+---------+---------------+--------------+-----------+--------------+---------------+-------------+------------------+---------+----------+------+---------+-------------------------+---------+----------------+
| id | version | firstname | lastname | login | password | password_salt | super_admin | preference_admin | address | zip_code | city | country | email | profile | post_login_url |
+------+---------+---------------+--------------+-----------+--------------+---------------+-------------+------------------+---------+----------+------+---------+-------------------------+---------+----------------+
| 1807 | 0 | zfirstname039 | zlastname039 | zlogin039 | zpassword039 | | 1 | 0 | NULL | NULL | NULL | NULL | zemail@thalasoft.com039 | NULL | NULL | 
+------+---------+---------------+--------------+-----------+--------------+---------------+-------------+------------------+---------+----------+------+---------+-------------------------+---------+----------------+
1 row in set (0.00 sec)

mysql> update admin set firstname='Toto', lastname='LeHeros', login='qwerty', super_admin=0, preference_admin=0, address=NULL, zip_code=NULL, city=NULL, country=NULL, email='stephane@thalasoft.com', profile=NULL, post_login_url=NULL where id=1807;
Query OK, 1 row affected (0.07 sec)
Rows matched: 1 Changed: 1 Warnings: 0

mysql> select * from admin; +------+---------+-----------+----------+--------+--------------+---------------+-------------+------------------+---------+----------+------+---------+------------------------+---------+----------------+
| id | version | firstname | lastname | login | password | password_salt | super_admin | preference_admin | address | zip_code | city | country | email | profile | post_login_url |
+------+---------+-----------+----------+--------+--------------+---------------+-------------+------------------+---------+----------+------+---------+------------------------+---------+----------------+
| 1807 | 0 | Toto | LeHeros | qwerty | zpassword039 | | 0 | 0 | NULL | NULL | NULL | NULL | stephane@thalasoft.com | NULL | NULL | 
+------+---------+-----------+----------+--------+--------------+---------------+-------------+------------------+---------+----------+------+---------+------------------------+---------+----------------+
1 row in set (0.00 sec)

mysql> select admin0_.id as id14_, admin0_.version as version14_, admin0_.address as address14_, admin0_.city as city14_, admin0_.country as country14_, admin0_.email as email14_, admin0_.firstname as firstname14_, admin0_.lastname as lastname14_, admin0_.login as login14_, admin0_.password as password14_, admin0_.password_salt as password11_14_, admin0_.post_login_url as post12_14_, admin0_.preference_admin as preference13_14_, admin0_.profile as profile14_, admin0_.super_admin as super15_14_, admin0_.zip_code as zip16_14_ from admin admin0_ where admin0_.email='stephane@thalasoft.com' limit 2;
+-------+------------+------------+---------+------------+------------------------+--------------+-------------+----------+--------------+----------------+------------+------------------+------------+-------------+-----------+
| id14_ | version14_ | address14_ | city14_ | country14_ | email14_ | firstname14_ | lastname14_ | login14_ | password14_ | password11_14_ | post12_14_ | preference13_14_ | profile14_ | super15_14_ | zip16_14_ |
+-------+------------+------------+---------+------------+------------------------+--------------+-------------+----------+--------------+----------------+------------+------------------+------------+-------------+-----------+
| 1807 | 0 | NULL | NULL | NULL | stephane@thalasoft.com | Toto | LeHeros | qwerty | zpassword039 | | NULL | 0 | NULL | 0 | NULL | 
+-------+------------+------------+---------+------------+------------------------+--------------+-------------+----------+--------------+----------------+------------+------------------+------------+-------------+-----------+
1 row in set (0.00 sec)

mysql> select admin0_.id as id14_, admin0_.version as version14_, admin0_.address as address14_, admin0_.city as city14_, admin0_.country as country14_, admin0_.email as email14_, admin0_.firstname as firstname14_, admin0_.lastname as lastname14_, admin0_.login as login14_, admin0_.password as password14_, admin0_.password_salt as password11_14_, admin0_.post_login_url as post12_14_, admin0_.preference_admin as preference13_14_, admin0_.profile as profile14_, admin0_.super_admin as super15_14_, admin0_.zip_code as zip16_14_ from admin admin0_ where admin0_.login='qwerty' limit 2;
+-------+------------+------------+---------+------------+------------------------+--------------+-------------+----------+--------------+----------------+------------+------------------+------------+-------------+-----------+
| id14_ | version14_ | address14_ | city14_ | country14_ | email14_ | firstname14_ | lastname14_ | login14_ | password14_ | password11_14_ | post12_14_ | preference13_14_ | profile14_ | super15_14_ | zip16_14_ |
+-------+------------+------------+---------+------------+------------------------+--------------+-------------+----------+--------------+----------------+------------+------------------+------------+-------------+-----------+
| 1807 | 0 | NULL | NULL | NULL | stephane@thalasoft.com | Toto | LeHeros | qwerty | zpassword039 | | NULL | 0 | NULL | 0 | NULL | 
+-------+------------+------------+---------+------------+------------------------+--------------+-------------+----------+--------------+----------------+------------+------------------+------------+-------------+-----------+
1 row in set (0.00 sec)

那么,为什么不在Java级别反映这一点呢?

我能够让它正常工作。我将在这里描述我的应用程序和集成测试

示例应用程序

示例应用程序具有与此问题相关的两个类和一个接口:

  • 应用程序上下文配置类
  • 实体类
  • 存储库接口
  • 下面将介绍这些类和存储库接口

    PersistenceContext
    类的源代码如下所示:

    import com.jolbox.bonecp.BoneCPDataSource;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.PropertySource;
    import org.springframework.core.env.Environment;
    import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
    import org.springframework.orm.jpa.JpaTransactionManager;
    import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
    import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
    import org.springframework.transaction.annotation.EnableTransactionManagement;
    
    import javax.sql.DataSource;
    import java.util.Properties;
    
    @Configuration
    @EnableTransactionManagement
    @EnableJpaRepositories(basePackages = "net.petrikainulainen.spring.datajpa.todo.repository")
    @PropertySource("classpath:application.properties")
    public class PersistenceContext {
    
        protected static final String PROPERTY_NAME_DATABASE_DRIVER = "db.driver";
        protected static final String PROPERTY_NAME_DATABASE_PASSWORD = "db.password";
        protected static final String PROPERTY_NAME_DATABASE_URL = "db.url";
        protected static final String PROPERTY_NAME_DATABASE_USERNAME = "db.username";
    
        private static final String PROPERTY_NAME_HIBERNATE_DIALECT = "hibernate.dialect";
        private static final String PROPERTY_NAME_HIBERNATE_FORMAT_SQL = "hibernate.format_sql";
        private static final String PROPERTY_NAME_HIBERNATE_HBM2DDL_AUTO = "hibernate.hbm2ddl.auto";
        private static final String PROPERTY_NAME_HIBERNATE_NAMING_STRATEGY = "hibernate.ejb.naming_strategy";
        private static final String PROPERTY_NAME_HIBERNATE_SHOW_SQL = "hibernate.show_sql";
    
        private static final String PROPERTY_PACKAGES_TO_SCAN = "net.petrikainulainen.spring.datajpa.todo.model";
    
        @Autowired
        private Environment environment;
    
        @Bean
        public DataSource dataSource() {
            BoneCPDataSource dataSource = new BoneCPDataSource();
    
            dataSource.setDriverClass(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_DRIVER));
            dataSource.setJdbcUrl(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_URL));
            dataSource.setUsername(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_USERNAME));
            dataSource.setPassword(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_PASSWORD));
    
            return dataSource;
        }
    
        @Bean
        public JpaTransactionManager transactionManager() {
            JpaTransactionManager transactionManager = new JpaTransactionManager();
    
            transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
    
            return transactionManager;
        }
    
        @Bean
        public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
            LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
    
            entityManagerFactoryBean.setDataSource(dataSource());
            entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
            entityManagerFactoryBean.setPackagesToScan(PROPERTY_PACKAGES_TO_SCAN);
    
            Properties jpaProperties = new Properties();
            jpaProperties.put(PROPERTY_NAME_HIBERNATE_DIALECT, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_DIALECT));
            jpaProperties.put(PROPERTY_NAME_HIBERNATE_FORMAT_SQL, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_FORMAT_SQL));
            jpaProperties.put(PROPERTY_NAME_HIBERNATE_HBM2DDL_AUTO, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_HBM2DDL_AUTO));
            jpaProperties.put(PROPERTY_NAME_HIBERNATE_NAMING_STRATEGY, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_NAMING_STRATEGY));
            jpaProperties.put(PROPERTY_NAME_HIBERNATE_SHOW_SQL, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL));
    
            entityManagerFactoryBean.setJpaProperties(jpaProperties);
    
            return entityManagerFactoryBean;
        }
    }
    
    @Entity
    @Table(name="todos")
    public class Todo {
    
        public static final int MAX_LENGTH_DESCRIPTION = 500;
        public static final int MAX_LENGTH_TITLE = 100;
    
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private Long id;
    
        @Column(name = "description", nullable = true, length = MAX_LENGTH_DESCRIPTION)
        private String description;
    
        @Column(name = "title", nullable = false, length = MAX_LENGTH_TITLE)
        private String title;
    
        @Version
        private long version;
    }
    
    import net.petrikainulainen.spring.datajpa.todo.model.Todo;
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.data.jpa.repository.Modifying;
    import org.springframework.data.jpa.repository.Query;
    import org.springframework.data.repository.query.Param;
    
    import java.util.List;
    
    public interface TodoRepository extends JpaRepository<Todo, Long> {
    
        @Modifying
        @Query("Update Todo t SET t.title=:title WHERE t.id=:id")
        public void updateTitle(@Param("id") Long id, @Param("title") String title);
    }
    
    <dataset>
        <todos id="1" description="Lorem ipsum" title="Foo" version="0"/>
        <todos id="2" description="Lorem ipsum" title="Bar" version="0"/>
    </dataset>
    
    假设我们有一个名为
    Todo
    的简单实体,其源代码如下所示:

    import com.jolbox.bonecp.BoneCPDataSource;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.PropertySource;
    import org.springframework.core.env.Environment;
    import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
    import org.springframework.orm.jpa.JpaTransactionManager;
    import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
    import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
    import org.springframework.transaction.annotation.EnableTransactionManagement;
    
    import javax.sql.DataSource;
    import java.util.Properties;
    
    @Configuration
    @EnableTransactionManagement
    @EnableJpaRepositories(basePackages = "net.petrikainulainen.spring.datajpa.todo.repository")
    @PropertySource("classpath:application.properties")
    public class PersistenceContext {
    
        protected static final String PROPERTY_NAME_DATABASE_DRIVER = "db.driver";
        protected static final String PROPERTY_NAME_DATABASE_PASSWORD = "db.password";
        protected static final String PROPERTY_NAME_DATABASE_URL = "db.url";
        protected static final String PROPERTY_NAME_DATABASE_USERNAME = "db.username";
    
        private static final String PROPERTY_NAME_HIBERNATE_DIALECT = "hibernate.dialect";
        private static final String PROPERTY_NAME_HIBERNATE_FORMAT_SQL = "hibernate.format_sql";
        private static final String PROPERTY_NAME_HIBERNATE_HBM2DDL_AUTO = "hibernate.hbm2ddl.auto";
        private static final String PROPERTY_NAME_HIBERNATE_NAMING_STRATEGY = "hibernate.ejb.naming_strategy";
        private static final String PROPERTY_NAME_HIBERNATE_SHOW_SQL = "hibernate.show_sql";
    
        private static final String PROPERTY_PACKAGES_TO_SCAN = "net.petrikainulainen.spring.datajpa.todo.model";
    
        @Autowired
        private Environment environment;
    
        @Bean
        public DataSource dataSource() {
            BoneCPDataSource dataSource = new BoneCPDataSource();
    
            dataSource.setDriverClass(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_DRIVER));
            dataSource.setJdbcUrl(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_URL));
            dataSource.setUsername(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_USERNAME));
            dataSource.setPassword(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_PASSWORD));
    
            return dataSource;
        }
    
        @Bean
        public JpaTransactionManager transactionManager() {
            JpaTransactionManager transactionManager = new JpaTransactionManager();
    
            transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
    
            return transactionManager;
        }
    
        @Bean
        public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
            LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
    
            entityManagerFactoryBean.setDataSource(dataSource());
            entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
            entityManagerFactoryBean.setPackagesToScan(PROPERTY_PACKAGES_TO_SCAN);
    
            Properties jpaProperties = new Properties();
            jpaProperties.put(PROPERTY_NAME_HIBERNATE_DIALECT, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_DIALECT));
            jpaProperties.put(PROPERTY_NAME_HIBERNATE_FORMAT_SQL, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_FORMAT_SQL));
            jpaProperties.put(PROPERTY_NAME_HIBERNATE_HBM2DDL_AUTO, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_HBM2DDL_AUTO));
            jpaProperties.put(PROPERTY_NAME_HIBERNATE_NAMING_STRATEGY, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_NAMING_STRATEGY));
            jpaProperties.put(PROPERTY_NAME_HIBERNATE_SHOW_SQL, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL));
    
            entityManagerFactoryBean.setJpaProperties(jpaProperties);
    
            return entityManagerFactoryBean;
        }
    }
    
    @Entity
    @Table(name="todos")
    public class Todo {
    
        public static final int MAX_LENGTH_DESCRIPTION = 500;
        public static final int MAX_LENGTH_TITLE = 100;
    
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private Long id;
    
        @Column(name = "description", nullable = true, length = MAX_LENGTH_DESCRIPTION)
        private String description;
    
        @Column(name = "title", nullable = false, length = MAX_LENGTH_TITLE)
        private String title;
    
        @Version
        private long version;
    }
    
    import net.petrikainulainen.spring.datajpa.todo.model.Todo;
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.data.jpa.repository.Modifying;
    import org.springframework.data.jpa.repository.Query;
    import org.springframework.data.repository.query.Param;
    
    import java.util.List;
    
    public interface TodoRepository extends JpaRepository<Todo, Long> {
    
        @Modifying
        @Query("Update Todo t SET t.title=:title WHERE t.id=:id")
        public void updateTitle(@Param("id") Long id, @Param("title") String title);
    }
    
    <dataset>
        <todos id="1" description="Lorem ipsum" title="Foo" version="0"/>
        <todos id="2" description="Lorem ipsum" title="Bar" version="0"/>
    </dataset>
    
    我们的存储库接口有一个名为
    updateTitle()
    的方法,它更新todo条目的标题。
    TodoRepository
    界面的源代码如下所示:

    import com.jolbox.bonecp.BoneCPDataSource;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.PropertySource;
    import org.springframework.core.env.Environment;
    import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
    import org.springframework.orm.jpa.JpaTransactionManager;
    import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
    import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
    import org.springframework.transaction.annotation.EnableTransactionManagement;
    
    import javax.sql.DataSource;
    import java.util.Properties;
    
    @Configuration
    @EnableTransactionManagement
    @EnableJpaRepositories(basePackages = "net.petrikainulainen.spring.datajpa.todo.repository")
    @PropertySource("classpath:application.properties")
    public class PersistenceContext {
    
        protected static final String PROPERTY_NAME_DATABASE_DRIVER = "db.driver";
        protected static final String PROPERTY_NAME_DATABASE_PASSWORD = "db.password";
        protected static final String PROPERTY_NAME_DATABASE_URL = "db.url";
        protected static final String PROPERTY_NAME_DATABASE_USERNAME = "db.username";
    
        private static final String PROPERTY_NAME_HIBERNATE_DIALECT = "hibernate.dialect";
        private static final String PROPERTY_NAME_HIBERNATE_FORMAT_SQL = "hibernate.format_sql";
        private static final String PROPERTY_NAME_HIBERNATE_HBM2DDL_AUTO = "hibernate.hbm2ddl.auto";
        private static final String PROPERTY_NAME_HIBERNATE_NAMING_STRATEGY = "hibernate.ejb.naming_strategy";
        private static final String PROPERTY_NAME_HIBERNATE_SHOW_SQL = "hibernate.show_sql";
    
        private static final String PROPERTY_PACKAGES_TO_SCAN = "net.petrikainulainen.spring.datajpa.todo.model";
    
        @Autowired
        private Environment environment;
    
        @Bean
        public DataSource dataSource() {
            BoneCPDataSource dataSource = new BoneCPDataSource();
    
            dataSource.setDriverClass(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_DRIVER));
            dataSource.setJdbcUrl(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_URL));
            dataSource.setUsername(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_USERNAME));
            dataSource.setPassword(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_PASSWORD));
    
            return dataSource;
        }
    
        @Bean
        public JpaTransactionManager transactionManager() {
            JpaTransactionManager transactionManager = new JpaTransactionManager();
    
            transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
    
            return transactionManager;
        }
    
        @Bean
        public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
            LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
    
            entityManagerFactoryBean.setDataSource(dataSource());
            entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
            entityManagerFactoryBean.setPackagesToScan(PROPERTY_PACKAGES_TO_SCAN);
    
            Properties jpaProperties = new Properties();
            jpaProperties.put(PROPERTY_NAME_HIBERNATE_DIALECT, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_DIALECT));
            jpaProperties.put(PROPERTY_NAME_HIBERNATE_FORMAT_SQL, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_FORMAT_SQL));
            jpaProperties.put(PROPERTY_NAME_HIBERNATE_HBM2DDL_AUTO, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_HBM2DDL_AUTO));
            jpaProperties.put(PROPERTY_NAME_HIBERNATE_NAMING_STRATEGY, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_NAMING_STRATEGY));
            jpaProperties.put(PROPERTY_NAME_HIBERNATE_SHOW_SQL, environment.getRequiredProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL));
    
            entityManagerFactoryBean.setJpaProperties(jpaProperties);
    
            return entityManagerFactoryBean;
        }
    }
    
    @Entity
    @Table(name="todos")
    public class Todo {
    
        public static final int MAX_LENGTH_DESCRIPTION = 500;
        public static final int MAX_LENGTH_TITLE = 100;
    
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private Long id;
    
        @Column(name = "description", nullable = true, length = MAX_LENGTH_DESCRIPTION)
        private String description;
    
        @Column(name = "title", nullable = false, length = MAX_LENGTH_TITLE)
        private String title;
    
        @Version
        private long version;
    }
    
    import net.petrikainulainen.spring.datajpa.todo.model.Todo;
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.data.jpa.repository.Modifying;
    import org.springframework.data.jpa.repository.Query;
    import org.springframework.data.repository.query.Param;
    
    import java.util.List;
    
    public interface TodoRepository extends JpaRepository<Todo, Long> {
    
        @Modifying
        @Query("Update Todo t SET t.title=:title WHERE t.id=:id")
        public void updateTitle(@Param("id") Long id, @Param("title") String title);
    }
    
    <dataset>
        <todos id="1" description="Lorem ipsum" title="Foo" version="0"/>
        <todos id="2" description="Lorem ipsum" title="Bar" version="0"/>
    </dataset>
    
    用于验证todo条目标题是否已更新的DbUnit数据集的名称称为toDoData-update.xml,其内容如下所示(出于某种原因,todo条目的版本未更新,但标题已更新。了解原因吗?)

    运行集成测试后,测试通过,todo条目的标题更新。我遇到的唯一问题是版本字段没有更新。你知道为什么吗


    我不理解这种描述有点模糊。如果您想获得有关为Spring数据JPA存储库编写集成测试的更多信息,可以阅读。

    我终于明白了发生了什么

    在保存对象的语句上创建集成测试时,建议刷新实体管理器,以避免任何误判,即避免测试运行正常,但在生产环境中运行时其操作将失败。事实上,测试可以正常运行,因为一级缓存没有刷新,也没有写入到数据库中。为了避免这种假阴性集成测试,请在测试体中使用显式刷新。请注意,生产代码不应该需要使用任何显式刷新,因为ORM的角色是决定何时刷新

    在update语句上创建集成测试时,可能需要清除实体管理器,以便重新加载一级缓存。实际上,update语句完全绕过一级缓存,直接写入数据库。第一级缓存随后不同步,并反映更新对象的旧值。要避免对象的这种陈旧状态,请在测试主体中使用显式清除。请注意,生产代码永远不需要使用任何显式清除,因为ORM的角色是决定何时清除


    我的测试现在运行正常。

    默认情况下,EntityManager不会自动刷新更改。您应该在查询语句中使用以下选项:

    @Modifying(clearAutomatically = true)
    @Query("update RssFeedEntry feedEntry set feedEntry.read =:isRead where feedEntry.id =:entryId")
    void markEntryAsRead(@Param("entryId") Long rssFeedEntryId, @Param("isRead") boolean isRead);
    

    我遇到了同样的问题,我试图执行一个更新查询,就像你做的一样-

    @Modifying
    @Transactional
    @Query(value = "UPDATE SAMPLE_TABLE st SET st.status=:flag WHERE se.referenceNo in :ids")
    public int updateStatus(@Param("flag")String flag, @Param("ids")List<String> references);
    
    修改 @交易的 @查询(value=“updatesample\u TABLE st SET st.status=:标志,其中se.referenceNo位于:id”) public int updateStatus(@Param(“flag”)字符串标志,@Param(“ids”)列表引用); 如果在主类上放置了
    @EnableTransactionManagement
    注释,则这将起作用。
    Spring 3.1引入了
    @EnableTransactionManagement
    注释,将在
    @Configuration
    类中使用,并启用事务支持。

    这里的根本问题是JPA的一级缓存。 来自JPA规范2.2版第3.1节。我强调:

    EntityManager实例与持久性上下文关联。持久性上下文是一组实体实例,其中对于任何持久性实体标识,都有一个唯一的实体实例

    这很重要,因为JPA跟踪对该实体的更改,以便将其刷新到数据库中。 作为一个副作用,它还意味着在单个持久性上下文中,实体只加载一次。 这就是为什么重新加载更改的实体没有任何效果

    您有两个选择如何处理此问题:

  • EntityManager
    中逐出实体。 这可以通过调用、用
    @modify(clearAutomatically=true)
    注释更新方法来完成,该方法会逐出所有实体。 确保先刷新对这些实体的更改,否则可能会丢失更改

  • 使用

  • 使用不同的持久性上下文加载实体。 最简单的方法是在单独的事务中完成。 对于Spring,这可以通过在从未使用
    @Transactional
    注释的bean调用的bean上使用
    @Transactional
    注释单独的方法来实现。 另一种方法是使用特别有效的