Hibernate - Nie mogę ogarnąc prostej relacji one to one.

Odpowiedz Nowy wątek
2018-08-06 15:01

Rejestracja: 1 rok temu

Ostatnio: 1 rok temu

1

Cześć, uczę się hibernate'a i relacji pomiędzy tabelami. Mam dwie encje - "Contact", która zawiera pola: id, imię i email oraz "Group", która zawiera pola id i nazwę grupy. Chcę zrobić prostą relację - każdy kontakt ma przypisaną jedną grupę (wiem, że to trochę nie logiczne, ale chcę pomału przećwiczyć wszystkie relacje). Ogólnie mi to wszystko nie działa i nie rozumiem dlaczego. Proszę o pomoc.

Kod wygląda następująco:

encja Kontakt:

package domain;

import javax.persistence.*;
import java.lang.reflect.Type;

@Entity
@Table(name = "contact")
public class Contact {
    @Id
    @GeneratedValue
    private int id;
    private String name;
    private String email;

    @OneToOne(mappedBy = "contact", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private Group group;

    public Contact(){}

    public Contact(String name, String email) {
        this.name = name;
        this.email = email;
    }

    @Override
    public String toString() {
        return "Contact{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", email='" + email + '\'' +
                ", group=" + group +
                '}';
    }

    // Getters and Setters

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Group getGroup() {
        return group;
    }

    public void setGroup(Group group) {
        this.group = group;
    }
}

encja Grupy:


package domain;

import javax.persistence.*;

@Entity
@Table(name = "group")
public class Group {

    @Id
    //@GeneratedValue
    private int groupId;

    private String groupName;

    @OneToOne
    @MapsId
    private Contact contact;

    // Constructors

    public Group(){}

    public Group(String groupName) {
        this.groupName = groupName;
    }

    // Getters and Setters

    public int getGroupId() {
        return groupId;
    }

    public void setGroupId(int groupId) {
        this.groupId = groupId;
    }

    public String getGroupName() {
        return groupName;
    }

    public void setGroupName(String groupName) {
        this.groupName = groupName;
    }

    public Contact getContact() {
        return contact;
    }

    public void setContact(Contact contact) {
        this.contact = contact;
    }
}

repozytorium Kontaktu:


package domain;

import hibernate.utils.HibernateUtils;
import org.hibernate.Session;

import java.util.List;

public class ContactRepository {

    public static void listAllContacts(){

        Session session = null;
        try {
            session = HibernateUtils.openSession();
            List<Contact> listEmployee = session.createQuery("SELECT c FROM Contact c").getResultList();

            if (listEmployee == null) {
                System.out.println("No employee found . ");
            } else {
                for (Contact contact : listEmployee) {
                    System.out.println(contact.toString());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if (session != null && session.isOpen()) {
                session.close();
            }
        }

    }

    public static void listContactById(Integer id){
        Session session = null;
        try {
            session = HibernateUtils.openSession();
            try{
                Contact contactById = session.find(Contact.class, id);
                System.out.println(contactById);
            } catch (NullPointerException e){
                System.out.println("No contact with id: " + id);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if (session != null && session.isOpen()) {
                session.close();
            }
        }
    }

    public static void addNewContact(Contact contact){

        Session session = null;
        try {
            session = HibernateUtils.openSession();
            session.getTransaction().begin();
            session.saveOrUpdate(contact);
            session.getTransaction().commit();
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            if (session != null && session.isOpen()) {
                session.close();
            }
        }
    }

    public static void editExistingContact(Contact contactToUpdate){

        Session session = null;
        try {
            session = HibernateUtils.openSession();
            session.getTransaction().begin();
            session.merge(contactToUpdate);
            session.getTransaction().commit();
        }catch (Exception e){
            e.printStackTrace();
        }finally{
            if (session != null && session.isOpen()) {
                session.close();
            }
        }

    }

    public static void deleteContact(int idToDelete){

        Session session = null;
        try {
            session = HibernateUtils.openSession();

            Contact contactToDelete = session.find(Contact.class, idToDelete);
            System.out.println(contactToDelete);

            session.getTransaction().begin();
            session.remove(contactToDelete);
            session.getTransaction().commit();

        }catch (Exception e){
            e.printStackTrace();
        }finally{
            if (session != null && session.isOpen()) {
                session.close();
            }
        }
    }
}

repozytorium Grupy:

package domain;

import hibernate.utils.HibernateUtils;
import org.hibernate.Session;

public class GroupRepository {

    public static void addNewGroup(Group group){

        Session session = null;
        try {
            session = HibernateUtils.openSession();
            session.getTransaction().begin();
            session.saveOrUpdate(group);
            session.getTransaction().commit();
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            if (session != null && session.isOpen()) {
                session.close();
            }
        }
    }
}

Klasa zawierająca metodę main:


import domain.Contact;
import domain.ContactRepository;
import domain.Group;
import domain.GroupRepository;
import hibernate.utils.HibernateUtils;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

import java.util.Scanner;

public class App {
    public static void main(String[] args) {

        Scanner scanner = new Scanner(System.in);
        boolean run = true;

        do {

            // MENU
            System.out.println("");
            System.out.println("MENU");
            System.out.println("=====================");
            System.out.println("1. List all contacts");
            System.out.println("2. Get contact by id");
            System.out.println("3. Add new contact");
            System.out.println("4. Update contact");
            System.out.println("5. Delete contact");
            System.out.println("6. Exit");
            System.out.println("=====================");
            System.out.println("");

            int choice = scanner.nextInt();

            switch (choice){

                case 1:
                    ContactRepository.listAllContacts();
                    break;
                case 2:
                    System.out.println("Type employee id:");
                    int id = scanner.nextInt();
                    ContactRepository.listContactById(id);
                    break;
                case 3:
                    System.out.println("Name: ");
                    String name = scanner.next();
                    System.out.println("Email: ");
                    String email = scanner.next();
                    System.out.println("Group: ");
                    String groupName = scanner.next();
                    Contact contact = new Contact(name, email);
                    Group group = new Group(groupName);
                    contact.setGroup(group);
                    ContactRepository.addNewContact(contact);
                    GroupRepository.addNewGroup(group);
                    break;

Błąd jaki dostaję przy próbie wypisania wszystkich danych z tabel (case 1):

Aug 05, 2018 6:49:04 PM org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions WARN: SQL Error: 1, SQLState: null Aug 05, 2018 6:49:04 PM org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions ERROR: [SQLITE_ERROR] SQL error or missing database (near "group": syntax error) javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: could not prepare statement at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:149) at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:157) at org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1423) at org.hibernate.query.Query.getResultList(Query.java:146) at domain.ContactRepository.listAllContacts(ContactRepository.java:15) at App.main(App.java:38) Caused by: org.hibernate.exception.GenericJDBCException: could not prepare statement at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:47) at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:111) at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$StatementPreparationTemplate.prepareStatement(StatementPreparerImpl.java:182) at org.hibernate.engine.jdbc.internal.StatementPreparerImpl.prepareQueryStatement(StatementPreparerImpl.java:148) at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:1985) at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1915) at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1893) at org.hibernate.loader.Loader.doQuery(Loader.java:938) at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:341) at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:311) at org.hibernate.loader.Loader.loadEntity(Loader.java:2282) at org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:61) at org.hibernate.loader.entity.EntityLoader.loadByUniqueKey(EntityLoader.java:144) at org.hibernate.persister.entity.AbstractEntityPersister.loadByUniqueKey(AbstractEntityPersister.java:2228) at org.hibernate.type.EntityType.loadByUniqueKey(EntityType.java:699) at org.hibernate.type.EntityType.resolve(EntityType.java:434) at org.hibernate.engine.internal.TwoPhaseLoad.doInitializeEntity(TwoPhaseLoad.java:165) at org.hibernate.engine.internal.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:125) at org.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:1152) at org.hibernate.loader.Loader.processResultSet(Loader.java:1011) at org.hibernate.loader.Loader.doQuery(Loader.java:949) at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:341) at org.hibernate.loader.Loader.doList(Loader.java:2692) at org.hibernate.loader.Loader.doList(Loader.java:2675) at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2507) at org.hibernate.loader.Loader.list(Loader.java:2502) at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:502) at org.hibernate.hql.internal.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:384) at org.hibernate.engine.query.spi.HQLQueryPlan.performList(HQLQueryPlan.java:216) at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1490) at org.hibernate.query.internal.AbstractProducedQuery.doList(AbstractProducedQuery.java:1445) at org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1414) ... 3 more Caused by: org.sqlite.SQLiteException: [SQLITE_ERROR] SQL error or missing database (near "group": syntax error) at org.sqlite.core.DB.newSQLException(DB.java:909) at org.sqlite.core.DB.newSQLException(DB.java:921) at org.sqlite.core.DB.throwex(DB.java:886) at org.sqlite.core.NativeDB.prepare_utf8(Native Method) at org.sqlite.core.NativeDB.prepare(NativeDB.java:127) at org.sqlite.core.DB.prepare(DB.java:227) at org.sqlite.core.CorePreparedStatement.<init>(CorePreparedStatement.java:41) at org.sqlite.jdbc3.JDBC3PreparedStatement.<init>(JDBC3PreparedStatement.java:30) at org.sqlite.jdbc4.JDBC4PreparedStatement.<init>(JDBC4PreparedStatement.java:19) at org.sqlite.jdbc4.JDBC4Connection.prepareStatement(JDBC4Connection.java:48) at org.sqlite.jdbc3.JDBC3Connection.prepareStatement(JDBC3Connection.java:263) at org.sqlite.jdbc3.JDBC3Connection.prepareStatement(JDBC3Connection.java:235) at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$5.doPrepare(StatementPreparerImpl.java:146) at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$StatementPreparationTemplate.prepareStatement(StatementPreparerImpl.java:172) ... 32 more

sqllite jest szczególną bazą (tu i ówdzie nadmiernie tolerancyjną / o innej koncepcji typ/wartość), po drugie jest bezserwerowa, można zgadywać że wiele robi driverze JDBC / dialekcie hiberatowym. Mam przekonanie, że JPA + bazy głównego szeregu nie wylatują w powietrze na słowie 'group'. BTW dialekt masz dobrze ustawiony? - AnyKtokolwiek 2018-08-06 17:28
Moim zdaniem dialekt jest ok, ponieważ zanim wprowadziłem relację wszystkie funkcje CRUD działały bezproblemowo. - s-kaczmarek 2018-08-06 17:31
Nie znam sqllite, ale może spróbuj zmienić kolejność dodawania elementów do repo, tj. najpierw grupa a później kontakt. Obecnie dodajesz kontakt z ustawioną grupą, której to grupy w bazie jeszcze nie ma. Może to jest problemem dla sqllite. - yarel 2018-08-06 19:04

Pozostało 580 znaków

2018-08-06 15:08

Rejestracja: 3 lata temu

Ostatnio: 1 godzina temu

Lokalizacja: U krasnoludów - pod górą

3

Możliwe, że błąd jest raczej zabawny. Group to słowo kluczowe SQL, więc nie może być nazwą tabeli.
Zmień na

@Entity
@Table(name = "grub")
public class Group {

jeden i pół terabajta powinno wystarczyć każdemu

Pozostało 580 znaków

2018-08-06 16:15

Rejestracja: 4 lata temu

Ostatnio: 4 godziny temu

1
jarekr000000 napisał(a):

Możliwe, że błąd jest raczej zabawny. Group to słowo kluczowe SQL, więc nie może być nazwą tabeli.

Oj tam zaraz nie może...

...po prostu nazwy będące słowami kluczowymi trzeba dać w square brackets ;)

CREATE TABLE [GROUP]

Wiedza to potęga

Pozostało 580 znaków

2018-08-06 17:24

Rejestracja: 1 rok temu

Ostatnio: 1 rok temu

0

Niestety zmiana nazwy tabeli nic nie dała

Pozostało 580 znaków

2018-08-06 18:30

Rejestracja: 4 lata temu

Ostatnio: 4 godziny temu

0

A jaki się teraz komunikat błedu wyświetla? Czytasz je chociaż?


Wiedza to potęga

Pozostało 580 znaków

2018-08-06 20:36

Rejestracja: 1 rok temu

Ostatnio: 1 rok temu

0

taki komunikat:

Aug 06, 2018 8:30:57 PM org.hibernate.Version logVersion
INFO: HHH000412: Hibernate Core {5.2.12.Final}
Aug 06, 2018 8:30:57 PM org.hibernate.cfg.Environment <clinit>
INFO: HHH000206: hibernate.properties not found
Aug 06, 2018 8:30:57 PM org.hibernate.boot.jaxb.internal.stax.LocalXmlResourceResolver resolveEntity
WARN: HHH90000012: Recognized obsolete hibernate namespace http://hibernate.sourceforge.net/hibernate-configuration. Use namespace http://www.hibernate.org/dtd/hibernate-configuration instead. Support for obsolete DTD/XSD namespaces may be removed at any time.
Aug 06, 2018 8:30:57 PM org.hibernate.annotations.common.reflection.java.JavaReflectionManager <clinit>
INFO: HCANN000001: Hibernate Commons Annotations {5.0.1.Final}
Aug 06, 2018 8:30:57 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl configure
WARN: HHH10001002: Using Hibernate built-in connection pool (not for production use!)
Aug 06, 2018 8:30:57 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001005: using driver [org.sqlite.JDBC] at URL [jdbc:sqlite:mydb.db]
Aug 06, 2018 8:30:57 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001001: Connection properties: {user=, password=****}
Aug 06, 2018 8:30:57 PM org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl buildCreator
INFO: HHH10001003: Autocommit mode: false
Aug 06, 2018 8:30:57 PM org.hibernate.engine.jdbc.connections.internal.PooledConnections <init>
INFO: HHH000115: Hibernate connection pool size: 20 (min=1)
Aug 06, 2018 8:30:57 PM org.hibernate.dialect.Dialect <init>
INFO: HHH000400: Using dialect: org.hibernate.dialect.SQLiteDialect
Aug 06, 2018 8:30:57 PM org.hibernate.engine.jdbc.env.internal.LobCreatorBuilderImpl useContextualLobCreation
INFO: HHH000423: Disabling contextual LOB creation as JDBC driver reported JDBC version [2] less than 4
Initial SessionFactory creation failed.org.hibernate.AnnotationException: Unknown mappedBy in: domain.Group.contact, referenced property unknown: domain.Contact.contact
Exception in thread "main" java.lang.ExceptionInInitializerError: Connection to database error!
at hibernate.utils.HibernateUtils.<clinit>(HibernateUtils.java:16)
at domain.ContactRepository.listAllContacts(ContactRepository.java:14)
at App.main(App.java:35)

Czytam, aczkolwiek niewiele mi one mówią. Końcówka mi się rzuciła w oczy:

Initial SessionFactory creation failed.org.hibernate.AnnotationException: Unknown mappedBy in: domain.Group.contact, referenced property unknown: domain.Contact.contact
Exception in thread "main" java.lang.ExceptionInInitializerError: Connection to database error!
at hibernate.utils.HibernateUtils.<clinit>(HibernateUtils.java:16)
at domain.ContactRepository.listAllContacts(ContactRepository.java:14)
at App.main(App.java:35)

Mimo wszystko nie wiem co ona oznacza, dodam, że taki komunikat pojawił się w momencie wyboru pierwszego case'a, czyli wypisania wszystkich danych.

Pozostało 580 znaków

2018-08-06 21:38

Rejestracja: 2 lata temu

Ostatnio: 4 godziny temu

Lokalizacja: Wlk. Brytania

0

Może spróbuj:

@OneToOne(mappedBy = "id", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private Group group;

https://www.google.co.uk/sear[...];sourceid=chrome&ie=UTF-8

Swoją drogą @jarekr000000 zamiast switch'a można by było użyć strategii? Dobrze myślę?

edytowany 1x, ostatnio: discoStar, 2018-08-06 21:41

Pozostało 580 znaków

2018-08-07 09:08

Rejestracja: 3 lata temu

Ostatnio: 1 godzina temu

Lokalizacja: U krasnoludów - pod górą

1
discoStar napisał(a):

Może spróbuj:


@OneToOne(mappedBy = "id", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private Group group;

Swoją drogą @jarekr000000 zamiast switch'a można by było użyć strategii? Dobrze myślę?

Ten mappedBy = "id" to raczej zły pomysł. Po prostu lepiej wywalić @MapsId (ciekawe skąd w ogóle OP to wytrzasnął?).
A co do strategii to można, ale nie wolno. Niech się lepiej początkujący skupi na nauce programowania, a nie na wzorcach.


jeden i pół terabajta powinno wystarczyć każdemu

Pozostało 580 znaków

2018-08-07 20:47

Rejestracja: 3 lata temu

Ostatnio: 3 dni temu

Lokalizacja: Siemianowice Śląskie

0

Zauważ, @s-kaczmarek, że teraz musimy zgadywać - co jest nie tak. Alternatywą jest podejrzeć uruchamiane polecenia sql. Najcześciej wtedy od razu widać, gdzie jest pies pogrzebany. Są na to 2 sposoby:

  1. Włączyć poziom logowania DEBUG na klasie org.hibernate.SQL (dokładnie nie pamiętam). Na początek można włączyć go globalnie (albo na org.hibernate), a potem okroić, jak już będzie wiadomo, co nas interesuje.
  2. Jest przełącznik JPA/Hibernate property, który powoduje wyświetlanie rozkazów SQL.

A jak mam zgadywać, to wyrzucam @MapsId i ustawiam nazwy kolumn wielkimi literami (@ColumnName). Czytam poza tym Vlada Michalceę.

Edit: Ciągle masz słowo kluczowe Group (jako nazwa kolumny).


Przeważnie ignoruję niezarejestrowanych użytkowników.
edytowany 2x, ostatnio: jarekczek, 2018-08-07 20:50

Pozostało 580 znaków

2018-08-07 23:19

Rejestracja: 1 rok temu

Ostatnio: 1 rok temu

0

Problem rozwiązany:

Faktycznie nazwa tabeli nie może być "group", ale to nie był jedyny problem. Z encji Group wywaliłem pole "contact", a w encji Contact nad polem "group" dodałem tylko adnotację @OneToOne bez dodatkowych parametrów. Na koniec od komentowałem zapis obiektu encji Group do bazy. Błędnie sądziłem, że przy relacji jeden-do-jednego musimy w obu encjach utworzyć pola przechowujące obiekty encji do których ma być relacja. Czegoś nowego się nauczyłem. Dzięki za pomoc!

unidirectional one-to-one vs bidirectional one-to-one. zależy co potrzeba - monterinio 2018-08-09 11:09

Pozostało 580 znaków

Odpowiedz

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0