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

Hibernate - Nie mogę ogarnąc prostej relacji one to one.
s-kaczmarek
  • Rejestracja:ponad 6 lat
  • Ostatnio:ponad 6 lat
  • Postów:16
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:

Kopiuj
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:

Kopiuj

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:

Kopiuj

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:

Kopiuj
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:

Kopiuj

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

AK
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?
s-kaczmarek
Moim zdaniem dialekt jest ok, ponieważ zanim wprowadziłem relację wszystkie funkcje CRUD działały bezproblemowo.
YA
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.
jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około godziny
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4707
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

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

jeden i pół terabajta powinno wystarczyć każdemu
Haskell
  • Rejestracja:prawie 10 lat
  • Ostatnio:12 miesięcy
  • Postów:4700
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 ;)

Kopiuj
CREATE TABLE [GROUP]

Zaglądali do kufrów, zaglądali do waliz, nie zajrzeli do d**y - tam miałem socjalizm. Czesław Miłosz
s-kaczmarek
  • Rejestracja:ponad 6 lat
  • Ostatnio:ponad 6 lat
  • Postów:16
0

Niestety zmiana nazwy tabeli nic nie dała

Haskell
  • Rejestracja:prawie 10 lat
  • Ostatnio:12 miesięcy
  • Postów:4700
0

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


Zaglądali do kufrów, zaglądali do waliz, nie zajrzeli do d**y - tam miałem socjalizm. Czesław Miłosz
s-kaczmarek
  • Rejestracja:ponad 6 lat
  • Ostatnio:ponad 6 lat
  • Postów:16
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.

discoStar
  • Rejestracja:ponad 7 lat
  • Ostatnio:około 3 lata
  • Lokalizacja:Wlk. Brytania
  • Postów:92
0

Może spróbuj:

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

https://www.google.co.uk/search?q=Unknown+mappedBy+referenced+property+unknown&oq=Unknown+mappedBy+referenced+property+unknown&aqs=chrome..69i57j0l5.3736j0j7&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
jarekr000000
  • Rejestracja:ponad 8 lat
  • Ostatnio:około godziny
  • Lokalizacja:U krasnoludów - pod górą
  • Postów:4707
1
discoStar napisał(a):

Może spróbuj:

Kopiuj
@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
jarekczek
  • Rejestracja:prawie 8 lat
  • Ostatnio:ponad 4 lata
  • Lokalizacja:Siemianowice Śląskie
  • Postów:500
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
s-kaczmarek
  • Rejestracja:ponad 6 lat
  • Ostatnio:ponad 6 lat
  • Postów:16
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!

MO
unidirectional one-to-one vs bidirectional one-to-one. zależy co potrzeba

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.