Я работаю над REST API, используя Spring boot, который будет управлять аукционом предметов.
Пользователи, участвующие в аукционе, будут выделены в отдельный Таблица пользователей.
Предметы для аукциона будут в Таблица Auction_items вместе с временем начала и окончания аукциона, победитель будет нулевым, если аукцион все еще продолжается.
Все ставки, сделанные пользователями для соответствующих товаров, находятся в Таблица ставок.
Я уже пробовал разные способы (cron, fixedRate, fixedDelay) для планирования задачи, получил одинаковую ошибку для всех из них, Я подозреваю, что это может быть проблема с БД, из-за которой это происходит.
Ниже приведены таблицы и соответствующий класс модели, а также интерфейс сервиса и репозитория.
ТАБЛИЦА ПОЛЬЗОВАТЕЛЕЙ:
CREATE TABLE USERS
(
USER_ID SERIAL,
NAME VARCHAR(200),
EMAIL VARCHAR(200),
PASSWORD VARCHAR(200),
PRIMARY KEY(USER_ID)
);
ТАБЛИЦА AUCTION_ITEMS:
CREATE TABLE AUCTION_ITEMS
(
ITEM_ID SERIAL,
ITEM_NAME VARCHAR(200),
ITEM_DESCRIPTION TEXT,
START_TIME TIMESTAMP,
END_TIME TIMESTAMP,
STARTING_AMOUNT INT,
WINNER INT,
PRIMARY KEY(ITEM_ID),
FOREIGN KEY(WINNER) REFERENCES USERS(USER_ID) ON DELETE CASCADE
);
ТАБЛИЦА ПРЕДЛОЖЕНИЙ:
CREATE TABLE BIDS
(
BID_ID SERIAL,
ITEM_ID INT,
USER_ID INT,
AMOUNT INT,
PRIMARY KEY(BID_ID),
FOREIGN KEY(ITEM_ID) REFERENCES AUCTION_ITEMS(ITEM_ID) ON DELETE
CASCADE,
FOREIGN KEY(USER_ID) REFERENCES USERS(USER_ID) ON DELETE CASCADE
);
Ниже приведены классы моделей для приведенных выше таблиц:
Пользователь.java:
@Entity
@Table(name = "USERS")
public class User {
@Id
private Integer user_id;
private String name;
private String email;
private String password;
//getter setter methods
}
Пункт.java:
@Entity
@Table(name = "AUCTION_ITEMS")
public class Item {
@Id
private Integer item_id;
private String item_name;
private String item_description;
private Timestamp start_time;
private Timestamp end_time;
private int starting_amount;
@ManyToOne
@JoinColumn(name = "winner")
private User user;
//getter setter methods
}
Ставка.java:
@Entity
@Table(name = "BIDS")
public class Bid {
@Id
private Integer bid_id;
@ManyToOne
@JoinColumn(name = "item_id")
private Item item;
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
private int amount;
//getter setter nethods
}
Ниже приведен класс обслуживания, в котором я определяю Метод запланированной задачи() для запуска и вычисления победителя предмета, выставленного на аукцион, по истечении времени окончания аукциона:
ItemService.java:
@Service
public class ItemService {
@Autowired
private ItemRepository itemRepository;
@Autowired
private UserRepository userRepository;
public List<Item> getAllItems(){
return (List<Item>) itemRepository.findAll();
}
public Object getItem(Integer id) {
Item i = itemRepository.findById(id).orElse(null);
if (i.getUser()!=null) {
return i.getUser();
}
else {
//will be returning highest bid amount for that particular item
return itemRepository.getMaxBid(id);
}
}
@Scheduled(cron = "20 36 17 * * ?")
public void scheduledTask() {
List<Item> listOfItems = (List<Item>)itemRepository.findAll();
System.out.println("going to update DB");
for(Item i : listOfItems) {
Timestamp time = new Timestamp(System.currentTimeMillis());
if (time.equals(i.getEnd_time()) || time.after(i.getEnd_time())) {
if (i.getUser() == null) {
Integer item_id = i.getItem_id();
Integer winner_id = itemRepository.findWinner(item_id);
User u= userRepository.findById(winner_id).orElse(null);
i.setUser(u);
System.out.println("updated");
}
}
}
}
}
ItemRepository.java:
public interface ItemRepository extends CrudRepository<Item,Integer>{
@Query(value = "select max(b.amount) from bids as b where b.item_id=?1",
nativeQuery=true )
public Integer getMaxBid(Integer item_id);
@Query(value = "select b.user_id from bids as b where b.item_id=?1 AND
b.amount = (select max(amount) from bids);", nativeQuery=true)
public Integer findWinner(Integer id);
Ниже ошибка, которую я получаю один раз, приходит время обновить данные в таблице:
2019-04-22 12:55:30.123 INFO 9100 --- [ scheduling-1] >o.h.h.i.QueryTranslatorFactoryInitiator : HHH000397: Using >ASTQueryTranslatorFactory going to update DB 2019-04-22 12:55:30.464 ERROR 9100 --- [ scheduling-1] ?>o.s.s.s.TaskUtils$LoggingErrorHandler : Unexpected error occurred in >scheduled task.
org.springframework.dao.InvalidDataAccessApiUsageException: >org.hibernate.QueryException: JPA-style positional param was not an integral >ordinal; nested exception is java.lang.IllegalArgumentException: >org.hibernate.QueryException: JPA-style positional param was not an integral >ordinal at >org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExcepti>onIfPossible(EntityManagerFactoryUtils.java:373) ~[spring-orm->5.1.6.RELEASE.jar:5.1.6.RELEASE] at >org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPo>ssible(HibernateJpaDialect.java:255) ~[spring-orm->]>5.1.6.RELEASE.jar:5.1.6.RELEASE] at >org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExcepti>onIfPossible(AbstractEntityManagerFactoryBean.java:527) ~[spring-orm->5.1.6.RELEASE.jar:5.1.6.RELEASE] at >org.springframework.dao.support.ChainedPersistenceExceptionTranslator.transla>teExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61) ~ [spring-tx-5.1.6.RELEASE.jar:5.1.6.RELEASE] at >org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAcce>ssUtils.java:242) ~[spring-tx-5.1.6.RELEASE.jar:5.1.6.RELEASE] at >org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.in>voke(PersistenceExceptionTranslationInterceptor.java:153) ~[spring-tx->5.1.6.RELEASE.jar:5.1.6.RELEASE] at >org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(Reflecti>veMethodInvocation.java:186) ~[spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE] at >org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcess>or$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPos>tProcessor.java:138) ~[spring-data-jpa-2.1.6.RELEASE.jar:2.1.6.RELEASE] at >org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(Reflecti>veMethodInvocation.java:186) ~[spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE] at >org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(Expose>InvocationInterceptor.java:93) ~[spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE] at >org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(Reflecti>veMethodInvocation.java:186) ~[spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE] at >org.springframework.data.repository.core.support.SurroundingTransactionDetect>orMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.ja>va:61) ~[spring-data-commons-2.1.6.RELEASE.jar:2.1.6.RELEASE] at >org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(Reflecti>veMethodInvocation.java:186) ~[spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE] at >org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProx>y.java:212) ~[spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE] at com.sun.proxy.$Proxy91.findUser(Unknown Source) ~[na:na] at auction.demo.service.ItemService.scheduledTask(ItemService.java:55) >~[classes/:na] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~ [na:1.8.0_121] at >sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) >~[na:1.8.0_121] at >sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.>java:43) ~[na:1.8.0_121] at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_121] at >org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledM>ethodRunnable.java:84) ~[spring-context-5.1.6.RELEASE.jar:5.1.6.RELEASE] at >org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(De>legatingErrorHandlingRunnable.java:54) ~[spring-context->5.1.6.RELEASE.jar:5.1.6.RELEASE] at >org.springframework.scheduling.concurrent.ReschedulingRunnable.run(Rescheduli>ngRunnable.java:93) [spring-context-5.1.6.RELEASE.jar:5.1.6.RELEASE] at >java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [na:1.8.0_121] at java.util.concurrent.FutureTask.run(FutureTask.java:266) [na:1.8.0_121] at >java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$2>01(ScheduledThreadPoolExecutor.java:180) [na:1.8.0_121] at >java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Sche>duledThreadPoolExecutor.java:293) [na:1.8.0_121] at >java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:114>2) [na:1.8.0_121] at >java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:61>7) [na:1.8.0_121] at java.lang.Thread.run(Thread.java:745) [na:1.8.0_121]
Вопрос: Создать запланированную задачу, которая запускается в момент окончания аукциона по предмету и определяет победителя этого предмета из таблицы ставок и сохраняет идентификатор этого пользователя в столбце победителя таблицы Auction_items.
Я думаю, что причина вышеописанной ошибки в том, что у меня есть создал переменную-член пользователя в классе модели, но на стороне таблицы ожидается только идентификатор пользователя, а не полный экземпляр пользователя.
Даже если это причина, я не уверен, как ее решить, также если это не причина, будет ли приложение автоматически сохранять правильный user_id в столбце победителя, если правильный экземпляр пользователя установлен с помощью setUser().
Обратите внимание, что я использовал @EnabledScheduling в классе с методом main().
Обновлено: Вставлен новый ItemService.java, Item.java В Item.java я использовал тип данных User для переменной-члена, поскольку создание его как Integer не работало.
Да, это не сработало, возникло другое исключение: org.springframework.dao.InvalidDataAccessResourceUsageException: не удалось извлечь ResultSet
Я думаю, что тип возвращаемого значения недействителен, поэтому измените его на List<User> вместо пользователя.
возвращаемый тип какого метода, findUser возвращает только один экземпляр пользователя.




ItemRepository для получения объектов пользователя, что является неправильным использованием.findUser, поскольку в репозиториях уже есть реализация для findById.Итак, короткий ответ, создайте отдельный UserRepository, переместите findWinner метод туда и используйте существующий findById из UserRepository вместо findUser из ItemRepository
Да, есть отдельный USerRepository, и я использовал только метод findById, я просто включил все сюда, так как оба возвращают одно и то же, решил немного сократить его.
Как видите, CrudRepository принимает два параметра типа: тип идентификатора объекта и сам объект. Это делает репозиторий очень специфичным для одного объекта и, следовательно, его не следует использовать для работы с другими типами доменов.
хорошо, даже если у нас есть пользовательский запрос? Но, переходя к настоящему вопросу, я использовал метод userRepository.findById(winner_id), теперь я получаю это исключение, я думаю, что мы сужаемся до основной проблемы: не удалось вызвать метод init; вложенным исключением является javax.persistence.PersistenceException: [PersistenceUnit: по умолчанию] Невозможно построить Hibernate SessionFactory; вложенным исключением является org.hibernate.MappingException: не удалось определить тип для:auction.demo.models.User, в таблице:auction_items, для столбцов: [org.hibernate.mapping.Column(winner)]
Я полагаю, это связано с тем, что внешний ключ для пользователя в таблице аукционов имеет тип INT, однако первичный ключ в таблице пользователей — Serial
Да, я тоже это подозреваю, но не знаю, как это убрать.
вам просто нужно изменить тип столбца в AUCTION_ITEMS, либо запустить SQL непосредственно в вашей базе данных, либо, что еще лучше, добавить в качестве сценария миграции, если вы используете какой-либо инструмент миграции, такой как flyway или liquibase
но поскольку я использую столбец победителя в качестве внешнего ключа, у него есть user_id, который будет ссылаться на таблицу пользователей, не уверен, что мне следует обновить тип столбца до ....?
Столбец внешнего ключа будет иметь копию значения первичного ключа, поэтому, следовательно, тип столбца внешнего ключа должен соответствовать типу первичного ключа, на который он ссылается.
Вы предлагаете изменить серийный номер user_id на Integer в таблице Users?
В качестве обходного пути я изменил тип данных переменной пользователя в классе модели Item на Integer и в методе Service, просто сделав i.setWinner(winner_id); в конце концов. Это устраняет ошибку, выполнение доходит до этой точки и все, но она все еще не обновляется в БД.
нет, я предлагаю вам обновить тип столбца внешнего ключа (winner_id), чтобы он соответствовал типу первичного ключа таблицы пользователей.
на самом деле кажется, что int следует принимать еще и потому, что серийный номер - это тип целого числа: |
Да, это должно быть принято, хотя я явно изменил тип на целое число, но получил то же исключение.
можете ли вы обновить свои примеры кода выше, так как сейчас мы пытаемся исправить новую проблему, отличную от исходной (которую мы уже исправили)
Я вставил обновленный ItemService.java, Item.java. Как видите, я добавил 2 системных вывода в метод ScheduleTask, один из которых говорит «собирается обновить базу данных», а другой говорит «обновлено», на этот раз никаких ошибок не выдается, оба системных вывода напечатано, но обновление БД еще не сделано.
да, потому что вам нужно вызвать itemRepository.save(i) после i.setUser(u), чтобы сохранить изменения, внесенные вами в объект
Да, теперь это решено, я был очень близок к решению, но двигался по кругу :D
Давайте продолжить обсуждение в чате.
Хорошо, поэтому в методе ScheduleTask() ItemService.java управление достигало каждой части, и экземпляр элемента «i» также обновлялся, я думаю, мне просто нужно было сохранить это в БД с помощью itemRepository.save(i) Ниже приведена полная реализация метода ScheduleTask().
@Scheduled(cron = "0 17 11 * * ?")
public void scheduledTask() {
List<Item> listOfItems = (List<Item>)itemRepository.findAll();
System.out.println("going to update DB");
for(Item i : listOfItems) {
Timestamp time = new Timestamp(System.currentTimeMillis());
if (time.equals(i.getEnd_time()) || time.after(i.getEnd_time())) {
if (i.getUser() == null) {
Integer item_id = i.getItem_id();
Integer winner_id = itemRepository.findWinner(item_id);
User u= userRepository.findById(winner_id).orElse(null);
i.setUser(u);
itemRepository.save(i);//change in code, which seems to work now
System.out.println("printing item:");
System.out.println(i.getItem_description()+", "+i.getItem_name()+", "+i.getStarting_amount()+", "+i.getItem_id()+", "+i.getEnd_time()+", "+i.getStart_time()+", "+i.getUser().getUser_id());
System.out.println("updated");
}
}
}
}
Пробовали убрать точку с запятой в конце запроса?