为什么JPA在Postgres中的同一查询返回行时返回null?

时间:2018-01-26 16:45:23

标签: java hibernate jpa junit

我很难让我的JUnit测试工作。以下是代码段。 第一个是我的超类:

@MappedSuperclass
abstract class IdentifiedEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    ...
}

然后有两个下属:

@Entity
@Table(name = "\"user\"")
@AttributeOverride(name = "id", column = @Column(name = "user_id"))
public class User extends IdentifiedEntity {
    ...
}

@Entity
@Table(name = "meal")
@AttributeOverride(name = "id", column = @Column(name = "meal_id"))
public class Meal extends IdentifiedEntity {
    @ManyToOne(optional = false)
    @JoinColumn(name = "user_id")
    private final User user;
    ...
    @Override
    public String toString() {
        return getClass().getSimpleName() + '#' + getId() + "U#" + user.getId()
                + ':' + desc + '@' + when + '×' + calories;
    }
}

这是脚本,在PostgreSQL中创建DB(一个例外):

create table "user" (
  user_id serial primary key,
  ...
);

create table meal (
  meal_id serial primary key,
  user_id int not null references "user"(user_id),
  ...
);

最后是测试用例(其中的一部分):

@ContextConfiguration("classpath:/spring/spring-app.xml")
@Sql("classpath:/db/refill.sql")
@RunWith(SpringRunner.class)
@Transactional(REQUIRED)
public class JpaMealRepositoryImplTest {
    @Test
    public void updateWithValidMeal() throws Exception {
        meal.setId(ID_MEAL_ADMINS);
        repo.update(meal);
    }
}

repo

@Repository
public final class JpaMealRepositoryImpl implements MealRepository {
    @Override
    public void update(Meal meal) throws NotFoundException {
        log.info("update({})", meal);
        Preconditions.checkArgument(!meal.isNew(), "new");
        Meal m = em.find(Meal.class, meal.getId());
        log.debug("m={}", m);
        if (m == null || m.getUser().getId() != meal.getUser().getId()) {
            throw new NotFoundException("mealId=" + meal.getId() + "; userId=" + meal.getUser().getId());
        }
        m.setWhen(meal.getWhen());
        m.setDesc(meal.getDesc());
        m.setCalories(meal.getCalories());
    }
}

小心,现在是尖叫:日志

190216.874 I main/JpaMealRepositoryImpl - update(Meal#4U#1:Перекус@2018-01-26T19:02×300)
Hibernate: 
    select
        meal0_.meal_id as id1_0_0_,
        meal0_.calories as calories2_0_0_,
        meal0_."desc" as desc3_0_0_,
        meal0_.user_id as user_id5_0_0_,
        meal0_."when" as when4_0_0_,
        user1_.user_id as id1_1_1_,
        user1_.admin as admin2_1_1_,
        user1_.calories_per_day_limit as calories3_1_1_,
        user1_.email as email4_1_1_,
        user1_.enabled as enabled5_1_1_,
        user1_.name as name6_1_1_,
        user1_.password as password7_1_1_,
        user1_.registered_at as register8_1_1_ 
    from
        meal meal0_ 
    inner join
        "user" user1_ 
            on meal0_.user_id=user1_.user_id 
    where
        meal0_.meal_id=?
190216.877 D main/JpaMealRepositoryImpl - m=null

ru.javawebinar.topjava.repository.NotFoundException: mealId=4; userId=1

    at ru.javawebinar.topjava.repository.jpa.JpaMealRepositoryImpl.update(JpaMealRepositoryImpl.java:38)
    at ru.javawebinar.topjava.repository.jpa.JpaMealRepositoryImplTest.updateWithValidMeal(JpaMealRepositoryImplTest.java:61)
...

问题是:为什么190216.877 D main/JpaMealRepositoryImpl - m=null

如果我在Postgres中运行此查询,将?替换为meal_id(日志中显示为4),那么我会得到一行。只是我想要的一行。为什么em.find(...)会返回null

想法?

谢谢大家!

更新

我在启动每个测试类时使用refill.sql脚本填充数据库:

@Sql("classpath:/db/refill.sql")

以下是我在psql中发出的qwery:

select
    meal0_.meal_id as id1_0_0_,
    meal0_.calories as calories2_0_0_,
    meal0_."desc" as desc3_0_0_,
    meal0_.user_id as user_id5_0_0_,
    meal0_."when" as when4_0_0_,
    user1_.user_id as id1_1_1_,
    user1_.admin as admin2_1_1_,
    user1_.calories_per_day_limit as calories3_1_1_,
    user1_.email as email4_1_1_,
    user1_.enabled as enabled5_1_1_,
    user1_.name as name6_1_1_,
    user1_.password as password7_1_1_,
    user1_.registered_at as register8_1_1_ 
    from
        meal meal0_ 
    inner join
        "user" user1_ 
            on meal0_.user_id=user1_.user_id 
    where
        meal0_.meal_id=4;

结果是

4|400|Завтрак|1|2017-12-11 10:00:00|1|t|2000|admin@example.com|t|Admin|\x21232f297a57a5a743894a0e4a801fc3|2018-01-26 22:37:08.326818+00

2 个答案:

答案 0 :(得分:0)

首先,测试应该在隔离的环境中运行,而不是在每次运行时清除数据库。

如果您希望测试删除当前的PostgreSQL架构,那么您应该只使用Hibernate hibernate.hbm2ddl.auto配置属性。

如果您不希望Hibernate更改架构,则不应设置hibernate.hbm2ddl.auto属性。

但是,你要在每次运行时回滚吗?你是如何在这样的环境中工作的。

最好从一个干净的模式开始,用数据填充它,进行测试,最后清除它,因为你不想留下可能干扰其他测试的数据。

答案 1 :(得分:0)

民间!

我以这种方式解决了这个问题。

正如我写的原始超类是

@MappedSuperclass
abstract class IdentifiedEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    ...
}

recreate.sql

create table "user" (
  user_id serial primary key,
  ...
);
create table meal (
  meal_id serial primary key,
  user_id int not null references "user"(user_id),
  ...
);

此修复程序位于strategy的{​​{1}}。这是续订的@GeneratedValue(strategy = GenerationType.IDENTITY)

IdentifiedEntity

@MappedSuperclass @Access(FIELD) abstract class IdentifiedEntity { @Id @SequenceGenerator(name = "seqgen", sequenceName = "sequencer") @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seqgen") private int id; ... } 是可选的)

@Access(FIELD)

recreate.sql

create sequence sequencer; create table "user" ( user_id int primary key default nextval('sequencer'), ... ); create table meal ( meal_id int primary key default nextval('sequencer'), user_id int not null references "user"(user_id), ... );

refill.sql

在我看来,Hibernate中存在BUG,如果... alter sequence sequencer restart 1; ... 再现。

谢谢你们!