Я следил за учебником Spring по созданию REST с использованием HATEOAS по адресу: https://spring.io/guides/tutorials/rest/ и смешал его с JPA и MySQL DB (Maven). Когда я запускаю приложение, я могу видеть первые 2 таблицы в рабочей среде MySQL в порядке (хотя третья появляется из ниоткуда?). Если я выполняю GET / player, все работает нормально. Когда я выполняю запрос POST (http: // localhost: 8080 / Players) в Postman с телом JSON: {"playerName": "Pedro"}, я получаю статус 500, а Spring выдает ошибку: "Недостаточно значений переменных, доступных для разверните 'id'] с основной причиной ... «Я хотел бы добиться полных операций CRUD. Здесь возникает много сомнений, учитывая, что playerId - это автоинкремент, а параметр registrariondate - это TIMESTAMP. Это второстепенно, поскольку я предполагаю, что проблема возникает из-за использования в моем приложении PresentationModelAssembler, но я не совсем уверен, как обрабатывать ответы и петиции.
Журнал ошибки:
Hibernate:
insert
into
player
(player_name, registration_date)
values
(?, ?)
2021-04-02 11:20:03.781 ERROR 2312 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.IllegalStateException: java.lang.IllegalArgumentException: Not enough variable values available to expand 'id'] with root cause
java.lang.IllegalArgumentException: Not enough variable values available to expand 'id'
at org.springframework.web.util.UriComponents$VarArgsTemplateVariables.getValue(UriComponents.java:370) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.web.util.UriComponents.expandUriComponent(UriComponents.java:263) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.web.util.HierarchicalUriComponents$FullPathComponent.expand(HierarchicalUriComponents.java:917) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.web.util.HierarchicalUriComponents.expandInternal(HierarchicalUriComponents.java:434) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.web.util.HierarchicalUriComponents.expandInternal(HierarchicalUriComponents.java:52) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.web.util.UriComponents.expand(UriComponents.java:172) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.web.util.DefaultUriBuilderFactory$DefaultUriBuilder.build(DefaultUriBuilderFactory.java:403) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.hateoas.UriTemplate.expand(UriTemplate.java:272) ~[spring-hateoas-1.2.5.jar:1.2.5]
at org.springframework.hateoas.Link.expand(Link.java:361) ~[spring-hateoas-1.2.5.jar:1.2.5]
at org.springframework.hateoas.Link.toUri(Link.java:434) ~[spring-hateoas-1.2.5.jar:1.2.5]
at RESTApiJWTAuthMySQL.controllers.PlayerController.createNewPlayer(PlayerController.java:66) ~[classes/:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:564) ~[na:na]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:197) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:141) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106) ~[spring-webmvc-5.3.5.jar:5.3.5]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:894) ~[spring-webmvc-5.3.5.jar:5.3.5]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) ~[spring-webmvc-5.3.5.jar:5.3.5]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.3.5.jar:5.3.5]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1060) ~[spring-webmvc-5.3.5.jar:5.3.5]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:962) ~[spring-webmvc-5.3.5.jar:5.3.5]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.5.jar:5.3.5]
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909) ~[spring-webmvc-5.3.5.jar:5.3.5]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:652) ~[tomcat-embed-core-9.0.44.jar:4.0.FR]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.5.jar:5.3.5]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:733) ~[tomcat-embed-core-9.0.44.jar:4.0.FR]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.44.jar:9.0.44]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.5.jar:5.3.5]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.5.jar:5.3.5]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.5.jar:5.3.5]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.3.5.jar:5.3.5]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:893) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1707) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630) ~[na:na]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.44.jar:9.0.44]
at java.base/java.lang.Thread.run(Thread.java:832) ~[na:na]
Класс контроллера
@RestController
//@RequestMapping("/")
public class PlayerController {
@Autowired
private final PlayerRepository repository;
@Autowired
private final PlayerModelAssembler assembler;
PlayerController(PlayerRepository repository, PlayerModelAssembler assembler) {
this.repository = repository;
this.assembler = assembler;
}
@GetMapping("/players/")
public CollectionModel<EntityModel<Player>> all() {
List<EntityModel<Player>> players = repository.findAll().stream().map
(assembler::toModel).collect(Collectors.toList());
return CollectionModel.of(players, linkTo(methodOn(PlayerController.class).all()).withSelfRel());
}
@GetMapping("/players/{id}")
public EntityModel<Player> one(@PathVariable Long playerId) {
Player player = repository.findById(playerId).orElseThrow(() -> new PlayerNotFoundException(playerId));
return assembler.toModel(player);
}
@PostMapping(path = "/players", consumes = "application/json")
public ResponseEntity<?> createNewPlayer(@RequestBody Player newPlayer) {
EntityModel<Player> entityModel = assembler.toModel(repository.save(newPlayer));
return ResponseEntity.created(entityModel.getRequiredLink(IanaLinkRelations.SELF).toUri()).body(entityModel);
}
@PutMapping("/players/{id}")
public ResponseEntity<?> replacePlayer(@RequestBody Player newPlayer, @PathVariable Long playerId) {
Player updatedPlayer = repository.findById(playerId) //
.map(player -> {
player.setPlayerName(newPlayer.getPlayerName());
return repository.save(player);
}) //
.orElseGet(() -> {
newPlayer.setPlayerId(playerId);
return repository.save(newPlayer);
});
EntityModel<Player> entityModel = assembler.toModel(updatedPlayer);
return ResponseEntity //
.created(entityModel.getRequiredLink(IanaLinkRelations.SELF).toUri()) //
.body(entityModel);
}
@DeleteMapping("/players/{id}")
public ResponseEntity<?> deleteEmployee(@PathVariable Long playerId) {
repository.deleteById(playerId);
return ResponseEntity.noContent().build();
}
}
PlayerModelAssembler - класс
@Component
public class PlayerModelAssembler implements RepresentationModelAssembler<Player, EntityModel<Player>> {
@Override
public EntityModel<Player> toModel(Player player) {
return EntityModel.of(player, //
linkTo(methodOn(PlayerController.class).one(player.getPlayerId())).withSelfRel(),
linkTo(methodOn(PlayerController.class).all()).withRel("players"));
}
}
Класс сущности игрока
@Entity
@Table(name = "Player")
public class Player {
@Id
@GeneratedValue (strategy = GenerationType.IDENTITY)
private Long playerId;
@Column (name = "player_name")
private String playerName;
@CreationTimestamp
@Temporal(TemporalType.TIMESTAMP)
@Column (name = "registration_date", updatable = false)
private Date registrationDate;
@OneToMany (mappedBy = "player", cascade = CascadeType.ALL, orphanRemoval=true)
private List<DiceRoll> diceRolls = new ArrayList<>();
public Player() {
}
public Player(Long playerId, String playerName, Date registrationDate) {
this.playerId=playerId;
this.playerName = playerName;
this.registrationDate = registrationDate;
}
//getter&setters//
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof Player))
return false;
Player player = (Player) o;
return Objects.equals(this.playerId, player.playerId) && Objects.equals(this.playerName, player.playerName)
&& Objects.equals(this.registrationDate, player.registrationDate);
}
@Override
public int hashCode() {
return Objects.hash(this.playerId, this.playerName, this.registrationDate);
}
@Override
public String toString() {
return "Player{" + "id = " + this.playerId + ", name='" + this.playerName + '\'' + ", date of registration='" + this.registrationDate + '\'' + '}';
}
}
POM файл
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.4.10.Final</version><!--$NO-MVN-MAN-VER$-->
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.hateoas</groupId>
<artifactId>spring-hateoas</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
Schema.sql
USE `dicegame`;
DROP TABLE IF EXISTS `Player`;
CREATE TABLE IF NOT EXISTS `Player` (
`player_id` BIGINT PRIMARY KEY AUTO_INCREMENT,
`player_name` VARCHAR(45) NOT NULL,
`registration_date` TIMESTAMP DEFAULT CURRENT_TIMESTAMP);
DROP TABLE IF EXISTS `DiceRoll`;
CREATE TABLE IF NOT EXISTS `DiceRoll` (
`diceRoll_id` BIGINT PRIMARY KEY AUTO_INCREMENT,
`d1` INT(55) NOT NULL,
`d2` INT(55) NOT NULL,
`result` VARCHAR(45) NOT NULL,
`diceRoll_registration` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`player_id` BIGINT NOT NULL);
ALTER TABLE DiceRoll
ADD constraint FK_PLAYER_ID FOREIGN KEY (player_id)
REFERENCES Player (player_id);
Любая помощь или направление будут очень благодарны. Я вижу в учебнике, что петитон для завивки используется как: $ curl -v -X POST localhost: 8080 / Players -H 'Content-Type: application / json' -d '{"playerName": "Pedro"}', и он должен работать. Большое спасибо.
Спасибо за ответ: здесь вы можете найти весь проект, как сейчас. У меня нет dataloaderclass, так как мои исходные данные берутся из schema.sql и данных, вставленных в эти 2 таблицы.
В дополнение к моему ответу ниже см. Мой пул реквест о том, как использовать базу данных H2 в памяти, которая упрощает запуск на компьютере помощника. Не у всех помощников установлен MySql на компьютере.
Метод PlayerModelAssembler.toModel(Player player)
использует PlayerController.one(@PathVariable Long playerId)
для создания собственной ссылки.
Если атрибут name
аннотации @PathVariable
не указан, Spring ожидает, что имя параметра будет таким же, как имя, окруженное {}
в @GetMapping
.
В исходном коде имя параметра playerId
отличается от id
. Чтобы исправить это,
@GetMapping("/players/{id}")
public EntityModel<Player> one(@PathVariable("id") Long playerId) {
}
или же
@GetMapping("/players/{id}")
public EntityModel<Player> one(@PathVariable Long id) {
}
или же
@GetMapping("/players/{playerId}")
public EntityModel<Player> one(@PathVariable Long playerId) {
}
Пошел на последний: @GetMapping ("/ Players / {playerId}") public EntityModel <Player> one (@PathVariable Long playerId) {} Большое спасибо, у меня были некоторые ошибки в этом, еще раз спасибо
Настоятельно рекомендуется написать минимальное приложение Spring Boot, которое воспроизведет вашу проблему и затем загрузит его на Github, а не публиковать несколько фрагментов кода. Вот один пример. Он использует базу данных H2 в памяти и прецедент для демонстрации запроса.