#spring #hibernate #kotlin #jpa #reference
#весна #спящий режим #kotlin #jpa #ссылка
Вопрос:
Я основатель Spring, я хочу создать свой клиентский сервер для своего приложения Android.
Мои компоненты:
@Entity
data class Aliment(
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
val numAliment: Long,
val nomFrAliment: String,
val nomAnAliment: String,
val numGenre: Float,
@ManyToOne
@JoinColumn(name = "num_genre")
val genre : Genre
) {
constructor() : this(0, "inconnu", "inconnu", 0f, Genre()) {
}
}
@Entity
data class Genre(
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
val numGenre: Float,
val nomAnGenre: String,
val nomFrGenre: String
) {
constructor() : this(0.0f, "", "")
}
Мои таблицы БД MYSQL:
CREATE TABLE `aliment` (
`num_aliment` integer NOT NULL AUTO_INCREMENT,
`nom_fr_aliment` varchar(255) NOT NULL,
`nom_an_aliment` varchar(255) NOT NULL,
`num_genre` varchar(4) NOT NULL,
PRIMARY KEY (`num_aliment`),
FOREIGN KEY (`num_genre`) REFERENCES `genre`(`num_genre`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `genre` (
`num_genre` varchar(4) NOT NULL,
`nom_an_genre` varchar(128) NOT NULL,
`nom_fr_genre` varchar(128) NOT NULL,
PRIMARY KEY (`num_genre`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Что я хочу сделать, так это когда я запрашиваю какое-либо дополнение, значение genre: Genre будет автоматически обновляться с помощью ссылки на внешний ключ.
Как я могу это сделать?
Подробнее
По моему запросу в моем мобильном приложении: я вызываю свой http-запрос, и клиент возвращает мне JSONObject, но значение жанра равно нулю (я хочу, чтобы мой клиент искал корреспондента num_genre и помещал его в мой жанр значений). Итак, на данный момент я делаю другой запрос, чтобы взять корреспондента жанра и обновить это значение genre = null до корреспондента жанра.
На клиенте у меня есть:
Репозиторий
@Repository
interface AlimRepository : JpaRepository<Aliment, Long> {
fun findAlimentsByNomFrAlimentStartingWith(nomFrAliment: String): List<Aliment>
fun findAlimentsByNomFrAlimentContains(nomFrAliment: String): List<Aliment>
fun findAlimentsByNomAnAlimentStartsWith(nomAnAliment: String): List<Aliment>
fun findAlimentsByNumGenre(numAliment: Float): List<Aliment>
}
@Repository
interface GenreRepository : JpaRepository<Genre, Float>
And I have my Controllers:
@RestController
@RequestMapping("/alim")
class AlimController(private val alimRepository: AlimRepository) {
/**
* RETURN full list Aliments
*
* @link : http://localhost:8080/alim/all
*/
@GetMapping("/all")
fun getAllAliments(): List<Aliment> {
println("/alim/all")
return alimRepository.findAll()
}
/**
* RETURN alim by num aliment ( works like Id )
*
* @link : http://localhost:8080/alim/num/2000
*/
@GetMapping("/num/{numAliment}")
fun getAlimByNum(@PathVariable(value = "numAliment") alimId: Long): ResponseEntity<Aliment> {
println("/alim/num/$alimId")
return alimRepository.findById(alimId).map { aliment ->
ResponseEntity.ok(aliment)
}.orElse(ResponseEntity.notFound().build())
}
/**
* RETURN ListAlims by StartingWith nameFr
*
* @link : http://localhost:8080/alim/name_fr/pat
*/
@GetMapping("/name_fr_start/{nomFrAliment}")
fun getAlimsByNameFrStartingWith(@PathVariable("nomFrAliment") nomFrAliment: String): List<Aliment> {
println("/alim/name_fr_start/$nomFrAliment")
return alimRepository.findAlimentsByNomFrAlimentStartingWith(nomFrAliment)
}
/**
* RETURN ListAlims by contain nameFr
*
* @link : http://localhost:8080/alim/name_fr/pat
*/
@GetMapping("/name_fr_contain/{nomFrAliment}")
fun getAlimsByNameFrContains(@PathVariable("nomFrAliment") nomFrAliment: String): List<Aliment> {
println("/alim/name_fr_contain/$nomFrAliment")
return alimRepository.findAlimentsByNomFrAlimentContains(nomFrAliment)
}
/**
* RETURN ListAlims by containing nameEn
*
* @link : http://localhost:8080/alim/name_en/pota
*/
@GetMapping("/name_en/{nomAnAliment}")
fun getAlimsByNameEn(@PathVariable("nomAnAliment") nomAnAliment: String): List<Aliment> {
println("/alim/name_en/$nomAnAliment")
return alimRepository.findAlimentsByNomAnAlimentStartsWith(nomAnAliment)
}
/**
* RETURN listAlims find by numGenre
*
* @link : http://localhost:8080/alim/num_genre/20
*/
@GetMapping("/num_genre/{numGenre}")
fun getAlimByNumGenre(@PathVariable("numGenre") numGenre: Float): List<Aliment> {
println("/alim/num_genre/$numGenre")
return alimRepository.findAlimentsByNumGenre(numGenre)
}
}
@RestController
@RequestMapping("/genre")
class GenreController(private val genreRepository: GenreRepository) {
/**
* RETURN full list Genre
*
* @link : http://localhost:8080/genre/all
*/
@GetMapping("/all")
fun getAllGenre(): List<Genre> {
println("/genre/all")
return genreRepository.findAll()
}
/**
* RETURN genre by numGenre ( works like Id )
*
* @link : http://localhost:8080/genre/num_genre/23.5
*/
@GetMapping("/num_genre/{numGenre}")
fun getGenreByNumGenre(@PathVariable(value = "numGenre") numId: Float): ResponseEntity<Genre> {
println("/genre/num_genre/$numId")
return genreRepository.findById(numId).map { genre ->
ResponseEntity.ok(genre)
}.orElse(ResponseEntity.notFound().build())
}
}
Конечно, когда я пробую этот код, я получаю эту ошибку:
2020-12-03 15:17:06.143 ERROR 14044 --- [ main] o.s.boot.SpringApplication : Application run failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.DuplicateMappingException: Table [aliment] contains physical column name [num_genre] referred to by multiple logical column names: [num_genre], [numGenre]
Комментарии:
1. Что вы делаете, чтобы ожидать обновления жанра? Пожалуйста, покажите сайт вызова (где вы создаете Aliment / загружаете его или что-то еще).
2. Привет, я отвечаю на ответ, чтобы указать подробности
3. Привет! Пожалуйста, не публикуйте уточнения вопроса в качестве ответа. Пожалуйста, отредактируйте свой вопрос и добавьте (только соответствующие) детали. Код должен быть минимальным, но по-прежнему показывать вашу проблему.
4. Эй, я обновляю его.
Ответ №1:
Поскольку Aliment
@ManyToOne
с ним может быть связано много ( Genres
) . Информация, которая Genre
принадлежит which Aliment
, должна храниться в таблице Genre
, которая есть genre
.
genre
уже есть поле num_genre
, которое является его первичным ключом, и @JoinColumn(name = "num_genre")
вы указываете Hibernate хранить информацию, которая Genre
принадлежит which Aliment
в том же столбце, что невозможно.
Короче говоря, если вы удалите @JoinColumn(name = "num_genre")
, он уже должен работать, потому что Hibernate создаст alignment_id
genre
для вас столбец в таблице.
Я бы также удалил numGenre
, поскольку он никоим образом не аннотирован, он все равно не будет управляться Hibernate, и связанный Genre
с ним объект уже будет представлен genre
свойством. Однако наличие этого свойства не должно вызывать ошибку.
Комментарии:
1. Клиент запускается, но он говорит: ошибка при выполнении DDL «alter table aliment добавить ограничение FKdg2xocbcyc7qdlumg6lejxm6f внешний ключ (genre_num_genre) ссылается на жанр (num_genre)» через оператор JDBC
2. Мое значение genre в моем Aliment по-прежнему равно нулю
3. @MakiX пожалуйста, удалите
numGenre
свойство изAliment
. Удалите базу данных (самый простой способ, если нечего переносить) и перезапустите приложение spring.4. Затем Hibernate создаст схему с нуля
5. @MakiX Это решает вашу проблему или не работает? 🙂