Как использовать класс Hibernate SchemaUpdate с JPA persistence.xml?
У меня есть основной метод, использующий SchemaUpdate для отображения на консоли каких таблиц для изменения/создания, и он отлично работает в моем проекте Hibernate:
public static void main(String[] args) throws IOException {
//first we prepare the configuration
Properties hibProps = new Properties();
hibProps.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("jbbconfigs.properties"));
Configuration cfg = new AnnotationConfiguration();
cfg.configure("/hibernate.cfg.xml").addProperties(hibProps);
//We create the SchemaUpdate thanks to the configs
SchemaUpdate schemaUpdate = new SchemaUpdate(cfg);
//The update is executed in script mode only
schemaUpdate.execute(true, false);
...
Я хотел бы повторно использовать этот код в проекте JPA, не имея файла hibernate.cfg.xml(и никакого файла .properties), но файла persistence.xml(автоопределяемого в каталоге META-INF, как указано в JPA).
Я пробовал эту слишком простую адаптацию,
Configuration cfg = new AnnotationConfiguration();
cfg.configure();
но это исключение не удалось.
Exception in thread "main" org.hibernate.HibernateException: /hibernate.cfg.xml not found
Кто-нибудь это сделал?
Спасибо.
Ответы
Ответ 1
Карием на правильном пути, но позвольте мне попытаться пояснить.
Предположим, что у вас есть стандартная конфигурация JPA в ваниле, где ничего не указано в Hibernate, кроме банок Hibernate в пути к классам. Если вы работаете в режиме начальной загрузки J2SE, у вас уже есть код, который выглядит примерно так: либо в Java, либо в виде конфигурации Spring и т.д.:
Map<String, Object> props = getJPAProperties();
EntityManagerFactory emf =
Persistence.createEntityManagerFactory("persistence-unit-name", props);
Чтобы запустить SchemaUpdate, просто используйте это вместо:
Map<String, Object> props = getJPAProperties();
Ejb3Configuration conf =
new Ejb3Configuration().configure("persistence-unit-name", props);
new SchemaUpdate(conf.getHibernateConfiguration()).execute(true, false);
Я не уверен, как это будет работать в среде контейнера, но в простой конфигурации J2SE или Spring, что все есть.
Ответ 2
Вы должны использовать Ejb3Configuration вместо обычной конфигурации. Обратитесь к документации менеджера объектов в конце раздела начальной загрузки в документации по спящему режиму.
(скопировано из исходного источника с незначительными изменениями)
Ejb3Configuration cfg = new Ejb3Configuration();
EntityManagerFactory emf =
cfg.addProperties(properties) // add some properties
.setInterceptor(myInterceptorImpl) // set an interceptor
.addAnnotatedClass(MyAnnotatedClass.class) // add a class to be mapped
.addClass(NonAnnotatedClass.class) // add an hbm.xml file using the Hibernate convention
.addResource("mypath/MyOtherCLass.hbm.xml") // add an hbm.xml file
.addResource("mypath/orm.xml" ) // add an EJB3 deployment descriptor
.configure("/mypath/hibernate.cfg.xml") // add a regular hibernate.cfg.xml
.buildEntityManagerFactory(); // create the entity manager factory
Как вы можете видеть, вы можете смешивать множество различных конфигураций.
Если это только для части обновления схемы, вы можете просто установить свойство в вашем файле persistence.xml: hibernate.hbm2ddl.auto
:
<persistence-unit name="app1">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<properties>
…
<property name="hibernate.hbm2ddl.auto" value="update"/>
</properties>
</properties>
См. здесь для еще нескольких ссылок.
Ответ 3
Спасибо тебе, Питер, твой ответ работал нормально.
Вот полный код нашего класса SchemaUpdater:
package reformyourcountry.dbupdate;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.hibernate.ejb.Ejb3Configuration;
import org.hibernate.tool.hbm2ddl.SchemaUpdate;
import reformyourcountry.misc.DateUtil;
/** Small utility to be run by a developer to identify the difference between
* its entities and its DB schema. It produces an SQL to be copy/pasted and applied
* on the DB manually. Each developers having its own DB, when a developer commits its
* Java code with new entity attributes (needing new DB columns), he also commits
* an updated SQL file with the SQL that other developers need to apply on their local DB.
* Later, when deploying the next version of the application in production,
* this SQL file with cumulated changes will be applied onto the production DB.
*
* Limitations:
* 1. the Hibernate schema update does not detect removed attributes.
* If you have to delete a column, you need to write the SQL manually;
*
* 2. the Hibernate schema update does not detect changes on existing columns.
* for example, if you add @Column(nullable=false), it will not generates an
* additional DB constraint.
*
* @author Cédric Fieux & John Rizzo & Aymeric Levaux
*
*/
public class SchemaUpdater {
@SuppressWarnings({ "deprecation", "unchecked" })
public static void main(String[] arg) throws IOException {
////// 1. Prepare the configuration (connection parameters to the DB, ect.)
// Empty map. We add no additional property, everything is already in the persistence.xml
Map<String,Object> map=new HashMap<String,Object>();
// Get the config from the persistence.xml file, with the unit name as parameter.
Ejb3Configuration conf = new Ejb3Configuration().configure("ConnectionPostgres",map);
SchemaUpdate schemaUpdate =new SchemaUpdate(conf.getHibernateConfiguration());
/////// 2. Get the SQL
// Before we run the update, we start capturing the console output (to add ";" later)
PrintStream initOut = System.out;
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(1024);
PrintStream newOut = new PrintStream(outputStream);
System.setOut(newOut);
//The update is executed in script mode only
schemaUpdate.execute(true, false);
//We reset the original out
System.setOut(initOut);
////// 3. Prints that SQL at the console with a good format (adding a ";" after each line).
System.out.println("--*******************************************Begin of SQL********************************************");
System.out.println("-- "+DateUtil.formatyyyyMMdd(new Date()));
BufferedReader ouReader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(outputStream.toByteArray())));
String str = ouReader.readLine();
while(str != null){ // For each (sometimes multiline) SQL statement
// now, str equals "".
str = ouReader.readLine(); //
while (str != null && !str.trim().equals("")) { // for each line of the same statement
System.out.println(); // previous line is finished.
System.out.print(str.toLowerCase());
str = ouReader.readLine();
}
// Statement is now finished
System.out.println(";");
}
System.out.println("--*******************************************End of SQL********************************************");
////// 4. Print eventual exceptions.
//If some exception occurred we display them
if(!schemaUpdate.getExceptions().isEmpty()){
System.out.println();
System.out.println("SOME EXCEPTIONS OCCURED WHILE GENERATING THE UPDATE SCRIPT:");
for (Exception e: (List<Exception>)schemaUpdate.getExceptions()) {
System.out.println(e.getMessage());
}
}
}
}