返回的键未设置 ID 字段

Posted

技术标签:

【中文标题】返回的键未设置 ID 字段【英文标题】:Id field does not set by returned key 【发布时间】:2016-01-28 23:56:14 【问题描述】:

首先:生成的 Id 不映射到实体对象。从 h2 日志中,似乎返回了 generatedKeys 结果集,并且 jpa 通过 resultSet.getInt(1) 获取密钥。但对象 id 字段未设置为返回值。

第二:有必要使用事务吗?现在我正在测试简单的 CRUD 命令并且不需要事务,我希望 DAO 中调用的每个方法都被提交。所以我设置 autocommit = true,但 hibernate 一直将它设置为 false 。如何解决?我希望在 DAO 中调用的方法连接到我自己的连接并测试数据。现在我刚刚创建了@AfterTransactional 并检查了数据,但这是一个非常糟糕的解决方案。

型号 @实体 @Table(name = "personal_phone_tbl") 公共类 PersonalPhone

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

@Column(name = "phone")
private String number;

public int getId() 
    return id;


public void setId(int id) 
    this.id = id;


public String getNumber() 
    return number;


public void setNumber(String number) 
    this.number = number;
  

@Repository
@Transactional
public class PersonalPhoneDao 
    @Autowired
    private EntityManager entityManager;

    public void create(PersonalPhone phone) 
        entityManager.merge(phone);
    

context.xml

<jee:jndi-lookup id="dataSource" jndi-name="jdbc/phonebook" expected-type="javax.sql.DataSource"/>

<bean id="entityManagerFactory"     class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="persistenceXmlLocation" value="classpath:persistence.xml"/>
    <property name="persistenceUnitName" value="phonebook-persistence"/>
    <property name="dataSource" ref="dataSource"/>
    <property name="packagesToScan" value="phonebook.*"/>
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
            <property name="showSql" value="true"/>
            <property name="databasePlatform" value="org.hibernate.dialect.mysql5Dialect"/>
        </bean>
    </property>
</bean>

<bean id="entityManager" class="org.springframework.orm.jpa.support.SharedEntityManagerBean">
    <property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>

<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

h2 日志

    INFO: Began transaction (1) for test context [DefaultTestContext@2267889d testClass = PersonalPhoneDaoTest, testInstance = com.getjavajob.web06.zhukm.PersonalPhoneDaoTest@3b3a0d10, testMethod = testInsert@AbstractDaoTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@4b06e721 testClass = PersonalPhoneDaoTest, locations = 'classpath:dao-context.xml, classpath:dao-context-overrides.xml', classes = '', contextInitializerClasses = '[]', activeProfiles = '', propertySourceLocations = '', propertySourceProperties = '', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]]; transaction manager [org.springframework.orm.jpa.JpaTransactionManager@602fb3d0]; rollback [false]
2016-01-29 02:48:50 jdbc[6]: 
/**/conn3.close();
2016-01-29 02:48:50 database: disconnecting session #6
2016-01-29 02:48:50 database: disconnected session #6
Hibernate: insert into personal_phone_tbl (phone) values (?)
2016-01-29 02:48:50 jdbc[4]: 
/**/conn1.prepareStatement("insert into personal_phone_tbl (phone) values (?)", 1);
2016-01-29 02:48:50 jdbc[4]: 
/**/PreparedStatement prep1 = conn1.prepareStatement("insert into personal_phone_tbl (phone) values (?)");
2016-01-29 02:48:50 jdbc[4]: 
/**/prep1.setString(1, "+7(909)6696578");
2016-01-29 02:48:50 jdbc[4]: 
/**/prep1.executeUpdate();
2016-01-29 02:48:50 lock: 1 exclusive write lock requesting for SYS
2016-01-29 02:48:50 lock: 1 exclusive write lock added for SYS
2016-01-29 02:48:50 lock: 1 exclusive write lock unlock SYS
2016-01-29 02:48:50 lock: 1 shared read lock unlock SYS
2016-01-29 02:48:50 lock: 4 shared read lock requesting for PERSONAL_PHONE_TBL
2016-01-29 02:48:50 lock: 4 shared read lock ok PERSONAL_PHONE_TBL
2016-01-29 02:48:50 jdbc[4]: 
/*SQL l:49 #:1 t:2*/insert into personal_phone_tbl (phone) values (?) 1: '+7(909)6696578';
2016-01-29 02:48:50 jdbc[4]: 
/**/ResultSet rs21 = prep1.getGeneratedKeys();
2016-01-29 02:48:50 jdbc[4]: 
/*SQL #:1*/SELECT SCOPE_IDENTITY() WHERE SCOPE_IDENTITY() IS NOT NULL;
2016-01-29 02:48:50 jdbc[4]: 
/**/rs21.next();
2016-01-29 02:48:50 jdbc[4]: 
/**/ResultSetMetaData rsMeta0 = rs21.getMetaData();
2016-01-29 02:48:50 jdbc[4]: 
/**/conn1.getCatalog();
2016-01-29 02:48:50 jdbc[4]: 
/**/rsMeta0.getColumnCount();
2016-01-29 02:48:50 jdbc[4]: 
/**/rs21.getInt(1);
2016-01-29 02:48:50 jdbc[4]: 
/**/rs21.close();
2016-01-29 02:48:50 jdbc[4]: 
/**/prep1.getWarnings();
2016-01-29 02:48:50 jdbc[4]: 
/**/prep1.clearWarnings();
2016-01-29 02:48:50 jdbc[4]: 
/**/prep1.getMaxRows();
2016-01-29 02:48:50 jdbc[4]: 
/**/prep1.getQueryTimeout();
2016-01-29 02:48:50 jdbc[4]: 
/*SQL l:58 #:1 t:1*/SELECT VALUE FROM INFORMATION_SCHEMA.SETTINGS WHERE NAME=? 1: 'QUERY_TIMEOUT';
2016-01-29 02:48:50 jdbc[4]: 
/**/prep1.close();
-----------------------------> personalPhone.getId()0
2016-01-29 02:48:50 jdbc[4]: 
/**/conn1.commit();
2016-01-29 02:48:50 lock: 4 shared read lock unlock PERSONAL_PHONE_TBL
2016-01-29 02:48:50 jdbc[4]: 
/*SQL */COMMIT;
2016-01-29 02:48:50 jdbc[4]: 
/**/conn1.setAutoCommit(true);
2016-01-29 02:48:50 jdbc[4]: 
/**/conn1.commit();
2016-01-29 02:48:50 jdbc[4]: 
/*SQL */COMMIT;
2016-01-29 02:48:50 jdbc[4]: 
/**/conn1.isClosed();
2016-01-29 02:48:50 jdbc[4]: 
/**/conn1.getWarnings();
2016-01-29 02:48:50 jdbc[4]: 
/**/conn1.clearWarnings();
2016-01-29 02:48:50 jdbc[4]: 
/**/conn1.isClosed();
2016-01-29 02:48:50 jdbc[4]: 
/**/conn1.clearWarnings();
Jan 29, 2016 2:48:50 AM org.springframework.test.context.transaction.TransactionContext endTransaction
INFO: Committed transaction for test context [DefaultTestContext@2267889d testClass = PersonalPhoneDaoTest, testInstance = com.getjavajob.web06.zhukm.PersonalPhoneDaoTest@3b3a0d10, testMethod = testInsert@AbstractDaoTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@4b06e721 testClass = PersonalPhoneDaoTest, locations = 'classpath:dao-context.xml, classpath:dao-context-overrides.xml', classes = '', contextInitializerClasses = '[]', activeProfiles = '', propertySourceLocations = '', propertySourceProperties = '', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]].
-----------------------------> @AfterTransaction
-----------------------------> personalPhone.getId()0
2016-01-29 02:48:50 database: connecting session #7 to mem:test
2016-01-29 02:48:50 jdbc[7]: 
/*SQL */SET MODE MYSQL;
2016-01-29 02:48:50 jdbc[7]: 
/*SQL */SET DB_CLOSE_DELAY -1;
2016-01-29 02:48:50 jdbc[7]: 
/**/Connection conn4 = DriverManager.getConnection("jdbc:h2:mem:test;MODE=MYSQL;DB_CLOSE_DELAY=-1", "SA", "");
2016-01-29 02:48:50 jdbc[7]: 
/**/conn4.setAutoCommit(false);
2016-01-29 02:48:50 jdbc[7]: 
/**/Statement stat2 = conn4.createStatement();
2016-01-29 02:48:50 jdbc[7]: 
/**/ResultSet rs22 = stat2.executeQuery("SELECT * FROM personal_phone_tbl");
2016-01-29 02:48:50 jdbc[7]: 
/*SQL #:1*/SELECT * FROM personal_phone_tbl;
2016-01-29 02:48:50 jdbc[7]: 
/**/rs22.next();
----------------------------->ResultSet
2016-01-29 02:48:50 jdbc[7]: 
   /**/rs22.getObject(1);
1
2016-01-29 02:48:50 jdbc[7]: 
/**/rs22.getObject(2);
+7(909)6696578
----------------------------->ResultSet
2016-01-29 02:48:50 jdbc[7]: 
/**/stat2.close();
2016-01-29 02:48:50 jdbc[7]: 
/**/conn4.close();
2016-01-29 02:48:50 database: disconnecting session #7
2016-01-29 02:48:50 database: disconnected session #7
2016-01-29 02:48:50 database: closing mem:test from shutdown hook
2016-01-29 02:48:50 database: disconnecting session #4
2016-01-29 02:48:50 database: closing mem:test
2016-01-29 02:48:50 lock: 1 exclusive write lock requesting for SYS
2016-01-29 02:48:50 lock: 1 exclusive write lock added for SYS
2016-01-29 02:48:50 lock: 1 exclusive write lock unlock SYS
2016-01-29 02:48:50 lock: 1 shared read lock unlock SYS
2016-01-29 02:48:50 database: closed

测试

@Before
public void createDb() 
    super.dropTables();
    super.createDb();
    personalPhone = new PersonalPhone();
    personalPhone.setNumber("123456");


@Override
public void testInsert() 
    personalPhoneDao.create(personalPhone);
    System.out.println("-----------------------------> personalPhone.getId()"+personalPhone.getId());
    connect();
    try (Statement statement = connection.createStatement()) 
        ResultSet resultSet = statement.executeQuery(SELECT);
        if(resultSet.next())
            System.out.println("----------------------------->ResultSet");
            System.out.println(resultSet.getObject(1));
            System.out.println(resultSet.getInt("id"));
            System.out.println(resultSet.getObject(2));
            System.out.println("----------------------------->ResultSet");
         else 
            Assert.fail();
        
     catch (SQLException e) 
        e.printStackTrace();
     finally 
        super.close();
    

表格

CREATE TABLE personal_phone_tbl(
id INT NOT NULL AUTO_INCREMENT,
phone VARCHAR(25) NOT NULL,
employee_id INT,
PRIMARY KEY(id),
FOREIGN KEY (employee_id) REFERENCES employee_tbl(id)
);

【问题讨论】:

【参考方案1】:

如果你使用

@GeneratedValue(strategy = GenerationType.IDENTITY)

生成的 Id 将从下一个 Id 开始,因此如果您有一行将 101 作为 id,则下一个将是 102

@GeneratedValue(strategy = GenerationType.AUTO)

将从第一个未使用的值开始。如果表中没有任何内容,他们也应该这样做。通常你应该使用 GenerationType.AUTO 来满足你的期望。

【讨论】:

仍然得到 get.id()=0 我想我需要 Identity 作为表中自动递增生成的 id?【参考方案2】:

我的错。我虽然 merge(entity) 修改了传递给它的对象。解决方法是改变create方法:

 public PersonalPhone create(PersonalPhone phone) 
        return entityManager.merge(phone);
    

【讨论】:

以上是关于返回的键未设置 ID 字段的主要内容,如果未能解决你的问题,请参考以下文章

为 ID 列生成的键在表记录中返回 null

Material-UI:“提供给类属性的键未实现”

mybatis返回一个count加一个字段该怎么设置返回resultType

如何使用 LINQ 中的键对值从单个字段中获取重复数据?

Laravel Eloquent:返回数组键作为字段 ID

如何使 JavaScript 中每个元素的 map 函数的键值递增