#java #spring-boot #jpa
#java #весенняя загрузка #jpa
Вопрос:
Я работаю над REST API, используя Spring boot, который будет управлять аукционом предметов.
Пользователи, участвующие в аукционе, будут в отдельной таблице Users
Предметы, которые будут выставлены на аукцион, будут в таблице Auction_items вместе со временем начала и окончания аукциона, победитель null, если аукцион все еще продолжается.
Все ставки, сделанные пользователями для соответствующих элементов, находятся в таблице ставок
Я уже пробовал разные способы (cron, fixedRate, fixedDelay) для планирования задачи, получил аналогичную ошибку для всех из них, я подозреваю, что может быть проблема с DB, из-за которой это происходит.
Ниже приведены таблицы и соответствующий класс модели, а также интерфейс сервиса и репозитория.
ТАБЛИЦА ПОЛЬЗОВАТЕЛЕЙ:
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
);
Ниже приведены классы моделей для приведенных выше таблиц:
User.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
}
Item.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
}
Bid.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
}
Below is the service class, where I am defining the scheduledTask() method to run and calculate the winner of Item auctioned once the end time of auction item is passed:
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);
Below is the error which I get once, the time comes to update the data in table:
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 не был целым > порядковым номером; вложенным исключением является java.lang.Исключение IllegalArgumentException: >org.hibernate.Исключение запроса: позиционный параметр в стиле JPA не был целым > порядковым номером в >org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExcepti>onIfPossible(EntityManagerFactoryUtils.java:373 ) ~[spring-orm->5.1.6.RELEASE.jar:5.1.6.RELEASE] в >org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPo>ssible(HibernateJpaDialect.java:255) ~[spring-orm->]>5.1.6.RELEASE.jar:5.1.6.RELEASE] в >org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExcepti>onIfPossible(AbstractEntityManagerFactoryBean.java:527 ) ~[spring-orm->5.1.6.RELEASE.jar:5.1.6.RELEASE] в >org.springframework.dao.support.ChainedPersistenceExceptionTranslator.transla>teExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61 ) ~ [spring-tx-5.1.6.RELEASE.jar:5.1.6.RELEASE] в >org.springframework.dao.support.DataAccessUtils.При необходимости переведите(DataAcce>ssUtils.java:242) ~[spring-tx-5.1.6.RELEASE.jar:5.1.6.RELEASE] в >org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.in>voke(PersistenceExceptionTranslationInterceptor.java:153 ) ~[spring-tx->5.1.6.RELEASE.jar:5.1.6.RELEASE] в >org.springframework.aop.framework.ReflectiveMethodInvocation.продолжить (Reflecti>veMethodInvocation.java: 186) ~[spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE] в >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] в >org.springframework.aop.framework.ReflectiveMethodInvocation.продолжить (Reflecti>veMethodInvocation.java: 186) ~ [spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE] в >org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(Expose>InvocationInterceptor.java:93 ) ~[spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE] в >org.springframework.aop.framework.ReflectiveMethodInvocation.продолжить (Reflecti>veMethodInvocation.java: 186) ~[spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE] в >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] в > org.springframework.aop.framework.ReflectiveMethodInvocation.продолжить (Reflecti>veMethodInvocation.java:186) ~[spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE] в >org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProx>y.java:212) ~[spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE] в com.sun.proxy.$Proxy91.findUser(неизвестный источник) ~ [na:na] в auction.demo.service.ItemService.ScheduledTask(ItemService.java: 55) > ~[classes/:na] в sun.reflect.NativeMethodAccessorImpl.invoke0(собственный метод) ~ [na: 1.8.0_121] в > sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) > ~ [na: 1.8.0_121] в > sun.отразить.Делегирование methodaccessorimpl.invoke(делегирование methodaccessorimpl.>java:43) ~[na:1.8.0_121] в java.lang.reflect.Метод.invoke(Method.java:498) ~ [na: 1.8.0_121] в>org.springframework.планирование.поддержка.ScheduledMethodRunnable.run(ScheduledM>ethodRunnable.java:84) ~[spring-context-5.1.6.RELEASE.jar:5.1.6.RELEASE] в >org.springframework.scheduling.support.Делегирование errorhandlingrunnable.run(Де> Легирование errorhandlingrunnable.java:54) ~[spring-context-> 5.1.6.RELEASE.jar:5.1.6.RELEASE] в >org.springframework.scheduling.concurrent.Перепланирование runnable.run(перепланирование> ngRunnable.java:93) [spring-context-5.1.6.RELEASE.jar:5.1.6.RELEASE] в> java.util.concurrent.Исполнители $RunnableAdapter.call(Executors.java:511) [na:1.8.0_121] в java.util.concurrent.FutureTask.run(FutureTask.java: 266) [na: 1.8.0_121] в> java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$ 2> 01(ScheduledThreadPoolExecutor.java:180) [na: 1.8.0_121] в> java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Sche> duledThreadPoolExecutor.java:293) [na: 1.8.0_121] в> 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] на java.lang.Thread.run(Thread.java:745) [na: 1.8.0_121]
Вопрос: Создайте запланированную задачу, которая запускается во время окончания аукциона по предмету и определяет победителя этого предмета из таблицы ставок и сохраняет идентификатор этого пользователя в столбце Победитель таблицы Auction_items.
Я думаю, что причина вышеупомянутой ошибки заключается в том, что я создал переменную-член пользователя в классе модели, но на стороне таблицы он ожидает только идентификатор пользователя, а не полный экземпляр пользователя.
Даже если это причина, я не уверен, как ее решить, также если это не причина, будет ли приложение автоматически сохранять правильный user_id в столбце winner, если правильный экземпляр пользователя установлен с помощью setUser().
Обратите внимание, что я использовал @EnabledScheduling в классе с методом main().
ОБНОВЛЕНИЕ: вставлено новое ItemService.java , Item.java В Item.java Я использовал тип данных User для переменной-члена, поскольку преобразование его в целое число не работало
Комментарии:
1. Вы пытались удалить точку с запятой в конце запроса?
2. Да, это не сработало, оно выдало другое исключение: org.springframework.dao.InvalidDataAccessResourceUsageException: не удалось извлечь результирующий набор
3. Я думаю, что возвращаемый тип недопустим, поэтому измените его на List<User> вместо user .
4. возвращаемый тип какого метода, findUser возвращает только один экземпляр пользователя.
Ответ №1:
- предполагается, что репозиторий управляет одним типом домена (сущностью), в вашем случае вы используете
ItemRepository
для извлечения пользовательских сущностей, что является неправильным использованием. - вам не нужно внедрять
findUser
метод, поскольку в репозиториях уже есть реализация дляfindById
. - Я бы рекомендовал вам попробовать использовать методы запроса JPA для таких простых запросов вместо того, чтобы прибегать к собственным запросам SQL.
Итак, короткий ответ, создайте отдельный метод UserRepository
, переместите findWinner
туда и используйте существующий findById
из UserRepository
вместо findUser
из ItemRepository
Комментарии:
1. Да, есть отдельный UserRepository, и я использовал только метод findById, я просто включил все здесь, поскольку оба возвращают одно и то же, думал немного сократить его
2. Как вы можете видеть, CrudRepository принимает два параметра типа: тип идентификатора для объекта и сам объект, это делает репозиторий очень специфичным для одного объекта и, следовательно, не должен использоваться для работы с другими типами доменов.
3. хорошо, даже если у нас есть пользовательский запрос? Но, переходя к настоящему вопросу, я использовал метод UserRepository.findById (winner_id), теперь я получаю это исключение, я думаю, что мы сужаемся до основной проблемы: не удалось вызвать метод инициализации; вложенное исключение — javax.persistence. Исключение PersistenceException: [PersistenceUnit: по умолчанию] Не удается создать Hibernate SessionFactory; вложенным исключением является org.hibernate. Исключение MappingException: не удалось определить тип для: auction.demo.models. Пользователь, в таблице: auction_items, для столбцов: [org.hibernate.mapping. Столбец (победитель)]
4. Я считаю, что это связано с тем, что внешний ключ для пользователя в таблице аукциона имеет тип
INT
однако первичный ключ в таблице Users являетсяSerial
5. Да, я тоже подозреваю это, но я не уверен, как это удалить.
Ответ №2:
Хорошо, итак, в методе ScheduledTask() ItemService.java управление достигало каждой части, и экземпляр элемента «i» также обновлялся, я думаю, мне просто нужно было сохранить это в БД с помощью itemRepository.save (i) Ниже приведена полная реализация метода ScheduledTask()
@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");
}
}
}
}