2-й набор глаз для списка сохранения JPA, который вызывает нарушение PK - PullRequest
2 голосов
/ 10 апреля 2019

У меня есть таблица Oracle db со столбцами, которые находятся в моем классе FoobarId. Все 3 поля в FooarId являются составным pk в моей таблице Foobar.

Я вставляю данные в порядке при первом запуске.Но когда я запускаю его снова, я получаю ORA-00001: уникальное ограничение, что мой PK нарушается.У меня есть подобный код в других проектах, который работает нормально.Похоже, что этот не обновляет запись, но вместо этого пытается сделать вставку снова.Что-то не так с моим встроенным идентификатором?

Мой класс сущностей:

@Entity
@Table(name = "FOOBAR_TABLE")
public class Foobar implements Serializable {

    private static final long serialVersionUID = 4290109857781985996L;

    private FoobarId foobarId;

    private int containerId;
    private String containerType;
    private String action;

    public Foobar() { }

    public Foobar(String itemName, int itemId, Date itemDate) {
        foobarId = new FoobarId(itemName, itemId, itemDate);
    }

    @EmbeddedId
    public FoobarId getFoobarId() {
        return foobarId;
    }

    public void setFoobarId(FoobarId foobarId) {
        this.foobarId = foobarId;
    }

    // getters and settters

}

Мой класс идентификаторов:

@Embeddable
public class FoobarId  implements Serializable{

    private static final long serialVersionUID = 7393136028821250311L;

    private int itemId;
    private String itemName;
    private Date itemDate;

    public FoobarId(){}

    public FoobarId(String itemName, int itemId, Date itemDate) {
        super();
        this.itemId = itemId;
        this.itemName = itemName;
        this.itemDate = itemDate;
    }

    // getters/setters

    @Override
 public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((itemDate == null) ? 0 : itemDate.hashCode());
    result = prime * result + ((itemName == null) ? 0 : itemName.hashCode());
    result = prime * result + itemId;
    return result;
}


@Override
public boolean equals(Object obj) {
    if (this == obj) {
        return true;
    }
    if (obj == null) {
        return false;
    }
    if (getClass() != obj.getClass()) {
        return false;
    }
    FoobarId other = (FoobarId) obj;
    if (itemDate == null) {
        if (other.itemDate != null) {
            return false;
        }
    } else if (!itemDate.equals(other.itemDate)) {
        return false;
    }
    if (itemName == null) {
        if (other.itemName != null) {
            return false;
        }
    } else if (!itemName.equals(other.itemName)) {
        return false;
    }
    if (itemId != other.itemId) {
        return false;
    }
    return true;
}
}

У меня есть необходимый интерфейс репозитория, который расширяет CrudRepository.

В моем классе обслуживания я сохраняю список элементов Foobar:

@Transactional
public void addList(List<Activity> itemList) {
    logger.info("Saving Activity List size of: " + itemList.size());
    activityRepository.save(itemList);
}

создать таблицу sql:

  CREATE TABLE "FOOBAR" 
   (    
    "ITEM_NAME" VARCHAR2(50 CHAR) NOT NULL ENABLE, 
    "ITEM_ID" NUMBER NOT NULL ENABLE, 
    "ITEM_DATE" DATE NOT NULL ENABLE, 
    "CONTAINER_ID" NUMBER, 
    "CONTAINER_TYPE" VARCHAR2(50 CHAR), 
    "ACTION" VARCHAR2(50 CHAR), 
     CONSTRAINT "FOOBAR_PK" PRIMARY KEY ("ITEM_NAME", "ITEM_ID", "ITEM_DATE")
     )

Ответы [ 2 ]

1 голос
/ 10 апреля 2019

«Даты» хитры, потому что семантика зависит от контекста.Например, java.util.Date включает время с точностью до миллисекунды, но тип данных Oracle DATE означает дату вместе со временем с точностью до одной секунды.Если ваш Java-код использует семантику равенства java.util.Date для столбца PRIMARY KEY с типом данных SQL DATE, вы увидите, что уникальные значения (с точки зрения JPA) сохраняются в базе данных, нарушая UNIQUEОграничение в любое время, когда отличается только доля секунды.

Как вы, вероятно, заметили, изменение типа данных столбца в БД на TIMESTAMP приведет к увеличению временного разрешения столбца -- и ваше ключевое пространство, решающее проблему.

1 голос
/ 10 апреля 2019

equals на Java Date объекты ненадежны при работе с базами данных (см., Например, этот вопрос ).

Измените метод equals, чтобы сравнить результат getTimeInMillis на каждую дату или, что еще лучше, не используйте дату в качестве части вашего первичного ключа.

Spring-Data-Jpa также поддерживает классы java.time, такие как LocalDate или LocalDateTime, которыелучше, чем Date.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...