Spring JPA Hibernate, как обновить автоматический объект значения @ManyToOne, на который уже есть ссылки в БД?

#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 Это решает вашу проблему или не работает? 🙂