💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
# 1.2.6\. 加载并存储对象 我们终于可以使用Hibernate来加载和存储对象了,编写一个带有`main()`方法的`EventManager`类: ``` package events; import org.hibernate.Session; import java.util.Date; import util.HibernateUtil; public class EventManager { public static void main(String[] args) { EventManager mgr = new EventManager(); if (args[0].equals("store")) { mgr.createAndStoreEvent("My Event", new Date()); } HibernateUtil.getSessionFactory().close(); } private void createAndStoreEvent(String title, Date theDate) { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); session.beginTransaction(); Event theEvent = new Event(); theEvent.setTitle(title); theEvent.setDate(theDate); session.save(theEvent); session.getTransaction().commit(); } } ``` 我们创建了个新的`Event`对象并把它传递给Hibernate。现在Hibernate负责与SQL打交道,并把`INSERT`命令传给数据库。在运行之前,让我们看一下处理`Session`和`Transaction`的代码。 一个`Session`就是个单一的工作单元。我们暂时让事情简单一些,并假设Hibernate`Session`和数据库事务是一一对应的。为了让我们的代码从底层的事务系统中脱离出来(此例中是JDBC,但也可能是JTA),我们使用Hibernate `Session`中的`Transaction` API。 `sessionFactory.getCurrentSession()`是干什么的呢?首先,只要你持有`SessionFactory`(幸亏我们有`HibernateUtil`,可以随时获得),大可在任何时候、任何地点调用这个方法。`getCurrentSession()`方法总会返回“当前的”工作单元。记得我们在`hibernate.cfg.xml`中把这一配置选项调整为"thread"了吗?因此,因此,当前工作单元被绑定到当前执行我们应用程序的Java线程。但是,这并非是完全准确的,你还得考虑工作单元的生命周期范围 (scope),它何时开始,又何时结束. `Session`在第一次被使用的时候,即第一次调用`getCurrentSession()`的时候,其生命周期就开始。然后它被Hibernate绑定到当前线程。当事务结束的时候,不管是提交还是回滚,Hibernate会自动把`Session`从当前线程剥离,并且关闭它。假若你再次调用`getCurrentSession()`,你会得到一个新的`Session`,并且开始一个新的工作单元。这种_线程绑定(thread-bound)_的编程模型(model)是使用Hibernate的最广泛的方式,因为它支持对你的代码灵活分层(事务划分可以和你的数据访问代码分离开来,在本教程的后面部分就会这么做)。 和工作单元的生命周期这个话题相关,Hibernate `Session`是否被应该用来执行多次数据库操作?上面的例子对每一次操作使用了一个`Session`,这完全是巧合,这个例子不是很复杂,无法展示其他方式。Hibernate `Session`的生命周期可以很灵活,但是你绝不要把你的应用程序设计成为_每一次_数据库操作都用一个新的Hibernate `Session`。因此就算下面的例子(它们都很简单)中你可以看到这种用法,记住_每次操作一个session_是一个反模式。在本教程的后面会展示一个真正的(web)程序。 关于事务处理及事务边界界定的详细信息,请参看[第 11 章 _事务和并发_](../Text/pr01_split_000.html "第 11 章 事务和并发")。在上面的例子中,我们也忽略了所有的错误与回滚的处理。 为第一次运行我们的程序,我们得在Ant的build文件中增加一个可以调用得到的target。 ``` <target name="run" depends="compile"> <java fork="true" classname="events.EventManager" classpathref="libraries"> <classpath path="${targetdir}"/> <arg value="${action}"/> </java> </target> ``` `action`参数(argument)的值是通过命令行调用这个target的时候设置的: ``` C:\hibernateTutorial\>ant run -Daction=store ``` 你应该会看到,编译以后,Hibernate根据你的配置启动,并产生一大堆的输出日志。在日志最后你会看到下面这行: ``` [java] Hibernate: insert into EVENTS (EVENT_DATE, title, EVENT_ID) values (?, ?, ?) ``` 这是Hibernate执行的`INSERT`命令,问号代表JDBC的绑定参数。如果想要看到绑定参数的值或者减少日志的长度,就要调整你在`log4j.properties`文件里的设置。 我们想要列出所有已经被存储的events,就要增加一个条件分支选项到main方法中去。 ``` if (args[0].equals("store")) { mgr.createAndStoreEvent("My Event", new Date()); } else if (args[0].equals("list")) { List events = mgr.listEvents(); for (int i = 0; i < events.size(); i++) { Event theEvent = (Event) events.get(i); System.out.println("Event: " + theEvent.getTitle() + " Time: " + theEvent.getDate()); } } ``` 我们也增加一个新的`listEvents()`方法: ``` private List listEvents() { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); session.beginTransaction(); List result = session.createQuery("from Event").list(); session.getTransaction().commit(); return result; } ``` 我们在这里是用一个HQL(Hibernate Query Language-Hibernate查询语言)查询语句来从数据库中加载所有存在的`Event`对象。Hibernate会生成适当的SQL,把它发送到数据库,并操作从查询得到数据的`Event`对象。当然,你可以使用HQL来创建更加复杂的查询。 现在,根据以下步骤来执行并测试以上各项: * 运行`ant run -Daction=store`来保存一些内容到数据库。当然,先得用hbm2ddl来生成数据库schema。 * 现在把`hibernate.cfg.xml`文件中hbm2ddl属性注释掉,这样我们就取消了在启动时用hbm2ddl来生成数据库schema。通常只有在不断重复进行单元测试的时候才需要打开它,但再次运行hbm2ddl会把你保存的一切都删掉(_drop_)——`create`配置的真实含义是:“在创建SessionFactory的时候,从schema 中drop 掉所有的表,再重新创建它们”。 如果你现在使用命令行参数`-Daction=list`运行Ant,你会看到那些至今为止我们所储存的events。当然,你也可以多调用几次`store`以保存更多的envents。 注意,很多Hibernate新手在这一步会失败,我们不时看到关于_Table not found_错误信息的提问。但是,只要你根据上面描述的步骤来执行,就不会有这个问题,因为hbm2ddl会在第一次运行的时候创建数据库schema,后继的应用程序重起后还能继续使用这个schema。假若你修改了映射,或者修改了数据库schema,你必须把hbm2ddl重新打开一次。