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