Generowanie tabel/kolumn przez JPA i Hibernate

0

Cześć!

Uczę się z kursu Antonio Goncalvesa z Pluralsight o JPA. On tam korzysta z EclipseLink'a, ja korzystam z Hibernate.
Niestety pliki przez niego udostępniane są niekompletne o skrypty sql (które są jakoś generowane, ale dostaję jakieś dziwne wyjątki z którymi długo walczyłem), więc stwierdziłem, że postawię bazę lokalnie z własnymi przykładami.
Korzystam z bazy H2.
Definicję pom.xml pomijam zakładając, że wszystko jest ok (a połączenie z bazą czy z aplikacji czy z okna Database w Intellij odbywa się bezproblemowo).

Mam sobie takie skrypty:

create table CD (
  id int primary key AUTO_INCREMENT not null,
  title varchar(50) not null ,
  description varchar(255) not null,
);

insert into CD (title, description) values ('title', 'description');

oraz

create table MUSICIAN (
  id int primary key auto_increment not null ,
  first_name varchar(50) not null ,
  last_name varchar(50) not null
);

insert into MUSICIAN(first_name, last_name)
values ('FirstName', 'LastName');

Odpowiadające im encje:

// imports ...

@Entity
public class CD {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String title;
    private String description;

    @OneToMany
    private Set<Musician> musicians = new HashSet<>();

    public CD() {
    }

    public CD(String title, String description, Musician ...musician) {
        this.title = title;
        this.description = description;
        if (musician == null || musician.length == 0) {
            throw new IllegalStateException("No musicians declared");
        } else {
            for (int i = 0; i < musician.length; i++) {
                System.out.println(musician[i]);
                musicians.add(musician[i]);
            }
        }
    }

    // Getters & setters

    @Override
    public String toString() {
        return "CD{" +
                "id=" + id +
                ", title='" + title + '\'' +
                ", description='" + description + '\'' +
                ", musician=" + musicians +
                '}';
    }
}

oraz

// imports ...
@Entity
public class Musician {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "first_name")
    private String firstName;

    @Column(name = "last_name")
    private String lastName;

    public Musician() {
    }

    public Musician(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    // Getters & setters

    @Override
    public String toString() {
        return "Musician{" +
                "id=" + id +
                ", firstName='" + firstName + '\'' +
                ", lastName='" + lastName + '\'' +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Musician musician = (Musician) o;
        return Objects.equals(id, musician.id) &&
                Objects.equals(lastName, musician.lastName);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, firstName, lastName);
    }
}

Przy próbie uruchomienia następującego programu:

// imports ...
public class Main {

    public static void main(String[] args) {
        EntityManagerFactory factory =
                Persistence.createEntityManagerFactory("h2");
        EntityManager manager = factory.createEntityManager();
        EntityTransaction transaction = manager.getTransaction();

        Musician musician = new Musician("ABC", "DEF");
        Musician musician1 = new Musician("GHI", "JKL");

        transaction.begin();
        manager.persist(musician);
        manager.persist(musician1);
        transaction.commit();

        CD cd = new CD("Architects", "New album", musician, musician1);

        transaction.begin();
        manager.persist(cd);
        transaction.commit();
    }
}

otrzymuję:

INFO: HHH000400: Using dialect: org.hibernate.dialect.H2Dialect
Musician{id=2, firstName='ABC', lastName='DEF'}
Musician{id=3, firstName='GHI', lastName='JKL'}
paź 26, 2018 1:27:33 AM org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions
WARN: SQL Error: 42102, SQLState: 42S02
paź 26, 2018 1:27:33 AM org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions
ERROR: Tabela "CD_MUSICIAN" nie istnieje
Table "CD_MUSICIAN" not found; SQL statement:
insert into CD_Musician (CD_id, musicians_id) values (?, ?) [42102-197]
paź 26, 2018 1:27:33 AM org.hibernate.internal.ExceptionMapperStandardImpl mapManagedFlushFailure
ERROR: HHH000346: Error during managed flush [org.hibernate.exception.SQLGrammarException: could not prepare statement]
Exception in thread "main" javax.persistence.RollbackException: Error while committing the transaction
    at org.hibernate.internal.ExceptionConverterImpl.convertCommitException(ExceptionConverterImpl.java:81)
    at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:101)
    at com.test.Main.main(Main.java:31)
Caused by: javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not prepare statement
    at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:154)
    at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:181)
    at org.hibernate.internal.ExceptionConverterImpl.convertCommitException(ExceptionConverterImpl.java:65)
    ... 2 more
Caused by: org.hibernate.exception.SQLGrammarException: could not prepare statement
    at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:106)
    at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:42)
    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:113)
    at org.hibernate.engine.jdbc.internal.StatementPreparerImpl$StatementPreparationTemplate.prepareStatement(StatementPreparerImpl.java:182)
    at org.hibernate.engine.jdbc.internal.StatementPreparerImpl.prepareStatement(StatementPreparerImpl.java:78)
    at org.hibernate.engine.jdbc.batch.internal.AbstractBatchImpl.buildBatchStatement(AbstractBatchImpl.java:136)
    at org.hibernate.engine.jdbc.batch.internal.AbstractBatchImpl.getBatchStatement(AbstractBatchImpl.java:125)
    at org.hibernate.persister.collection.AbstractCollectionPersister.recreate(AbstractCollectionPersister.java:1314)
    at org.hibernate.action.internal.CollectionRecreateAction.execute(CollectionRecreateAction.java:50)
    at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:604)
    at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:478)
    at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:356)
    at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39)
    at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1454)
    at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:511)
    at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:3283)
    at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2479)
    at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:473)
    at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:178)
    at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access$300(JdbcResourceLocalTransactionCoordinatorImpl.java:39)
    at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:271)
    at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:98)
    ... 1 more
Caused by: org.h2.jdbc.JdbcSQLException: Tabela "CD_MUSICIAN" nie istnieje
Table "CD_MUSICIAN" not found; SQL statement:
insert into CD_Musician (CD_id, musicians_id) values (?, ?) [42102-197]

Czy Hibernate nie powinien sam zająć się tworzeniem odpowiedniej tabeli wraz z kluczami? Czy muszę to robić ręcznie?

1

Raczej nie powinien.
"muszę to robić ręcznie" - to ty jesteś odpowiedzialny za projekt bazy, a ona nie ma powiązań między artystami a utworami.
Zachowanie którego oczekiwałeś, miałoby taki efekt uboczny, że tabele by były tworzone nawet jeśli by były nie pożądane.
Hibernate ma raczej tylko wesprzeć mapowanie między tabelami bazy a ich reprezentacją obiektową - a nie zastanawiać się czy kod w javie jest zły bo relacja nie istnieje w bazie czy też baza jest zła bo nie ma relacji a ktoś w kodzie napisał jakby była ... komputery są coraz mądrzejsze to może kiedyś będzie takie wsparcie.

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