Issue
Note. It's a heavily trimmed down version of the problematic code. I spent a lot of time making it. I believe it's pretty MREish. I do hope for a helpful answer
Here's my test
package by.afinny.credit.unit.repository;
import by.afinny.credit.entity.Card;
import by.afinny.credit.enumeration.CardStatus;
import by.afinny.credit.repository.CardRepository;
import org.assertj.core.api.SoftAssertions;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.jdbc.Sql;
import java.util.List;
import java.util.UUID;
@DataJpaTest
@ActiveProfiles("test")
@Sql(
executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD,
scripts = {"/schema-h2.sql", "/data-h2.sql"}
)
class CardRepositoryTest {
@Autowired
private CardRepository cardRepository;
private final static UUID ACCOUNT_ID = UUID.fromString("00000000-0000-0000-0000-000000000ac1");
@ParameterizedTest
@EnumSource(CardStatus.class)
void testFindByAccountIdAndCardStatusNot(CardStatus excludedCardStatus) {
List<Card> cards = cardRepository.findByAccountIdAndCardStatusNot(ACCOUNT_ID, excludedCardStatus); // never gets beyond this line, though
cards.forEach(card -> verifyProperties(card, excludedCardStatus));
}
private void verifyProperties(Card card, CardStatus excludedCardStatus) {
SoftAssertions.assertSoftly(softAssertions -> {
softAssertions.assertThat(card.getAccount().getId()).isEqualTo(ACCOUNT_ID);
softAssertions.assertThat(card.getCardStatus()).isNotEqualTo(excludedCardStatus);
});
}
}
Here are my scripts:
CREATE TABLE IF NOT EXISTS PUBLIC.account
(
id UUID PRIMARY KEY
);
CREATE TABLE IF NOT EXISTS PUBLIC.card
(
id UUID PRIMARY KEY,
account_id UUID NOT NULL REFERENCES account (id),
status VARCHAR(30) NOT NULL
);
CREATE TABLE IF NOT EXISTS PUBLIC.account
(
id UUID PRIMARY KEY
);
CREATE TABLE IF NOT EXISTS PUBLIC.card
(
id UUID PRIMARY KEY,
account_id UUID NOT NULL REFERENCES account (id),
status VARCHAR(30) NOT NULL
);
My entities:
package by.afinny.credit.entity;
import by.afinny.credit.enumeration.CardStatus;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import java.util.UUID;
@Entity
@Table(name = "card")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Card {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
private UUID id;
@Enumerated(EnumType.STRING)
@Column(name = "status", nullable = false, length = 30)
private CardStatus cardStatus;
@OneToOne(cascade = CascadeType.MERGE)
@JoinColumn(name = "account_id", referencedColumnName = "id")
private Account account;
}
package by.afinny.credit.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import java.util.UUID;
@Entity
@Table(name = "account")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Account {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
private UUID id;
@OneToOne(mappedBy = "account")
private Card card;
}
package by.afinny.credit.enumeration;
public enum CardStatus {
BLOCKED,
ACTIVE,
CLOSED,
NON_ACTIVE
}
package by.afinny.credit.repository;
import by.afinny.credit.entity.Card;
import by.afinny.credit.enumeration.CardStatus;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.UUID;
@Repository
public interface CardRepository extends JpaRepository<Card, UUID> {
List<Card> findByAccountIdAndCardStatusNot(UUID accountId, CardStatus excludedStatus);
}
Properties:
spring:
datasource:
url: jdbc:h2:mem:db;DB_CLOSE_DELAY=-1;MODE=MySQL;DATABASE_TO_LOWER=TRUE
driver-class-name: org.h2.Driver
username: sa
password:
jpa:
hibernate:
ddl-auto: none
show-sql: true
database-platform: org.hibernate.dialect.H2Dialect
properties:
hibernate:
format_sql: true
h2:
console:
enabled: false
settings:
web-allow-others: true
config:
activate:
on-profile: test
logging:
level:
org:
hibernate:
type: TRACE
springframework:
jdbc:
core: TRACE
datasource:
init: DEBUG
test:
context:
jdbc: DEBUG
I can include the pom as well if it's necessary, but I don't think so (basically all it has is starter-data-jpa, starter-test, lombok, h2)
The problem:
Caused by: org.hibernate.HibernateException: More than one row with the given identifier was found: 00000000-0000-0000-0000-000000000ac1, for class: by.afinny.credit.entity.Card
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:104)
at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:66)
at org.hibernate.persister.entity.AbstractEntityPersister.loadByUniqueKey(AbstractEntityPersister.java:2486)
Why does it see the foreign key to the account table as an identifier? I can't wrap my head around it. It should be valid for a table to refer to the same primary key of another table
Interestingly, it runs fine with ACTIVE, but fails in all other cases
What's the root of this problem? How do I fix it?
Solution
The error occurs because there are two candidates for the card field in Account entity.
It should be valid for a table to refer to the same primary key of another table
It is valid if you use @ManyToOne association type. For @OneToOne it's invalid.
Answered By - Anton Kozub
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.