Domain objects
See slide 35 for PizzaSize.java, a domain class and POJO, slide 27 for
part of PizzaOrder.java, showing Set<Topping> getToppings(). The domain
objects are created (in the service or DAO layers) by new, and are initially
unknown to the Hibernate runtime system. We have to tell Hibernate by calling
session.persist(order) for example, slide 40.
A “property” (Java Bean meaning) is named and typed data object obtainable from an object via getters and/or setters. For example, toppingName is a String property of Topping because Topping.java has method “String getToppingName()” (and also setToppingName, but that’s not crucial.) A property is similar to the idea of a UML attribute, but a property is more explicitly connected to getters and setters. A UML attribute could be a public field, but we try to avoid that. In UML, if a getter returns an Entity or Set<Entity>, it does not correspond to an attribute, but rather to (an end of) an association. It still represents a property in the Java Bean sense, but not the most elementary kind of property. Sometimes the association-related properties are called “navigational properties.”
Topping properties: int id, String toppingName
PizzaSize properties: int id, String sizeName
PizzaOrder properties: int id, int roomNumber, int
day, int status, also String statusString (a “computed property”, since it is
computed from int status) The property PizzaSize pizzaSize represents one end
of the PizzaOrder-PizzaSize association, and the property Set<Toppings>
toppings represents one end of the PizzaOrder-Topping association.
Note that Strings are considered to be
basic type in ORM, because Strings are held in char(n) or varchar(n) column
values, a basic database type. Any type
that can be mapped to a database type is treated this way, e.g. Timestamp.
Example Service layer method: runs transaction, creates a domain object, calls DAO methods it needs, catches exceptions (Hibernate throws RuntimeExceptions), calls rollbackAfterException, throws ServiceException
Note that domain objects are being passed down from presentation, but these objects were previously supplied to the presentation layer by service methods getToppings() and getPizzaSizes().
public void makeOrder(int roomNum, PizzaSize size, Set<Topping> toppings)
throws ServiceException {
try {
dbDAO.startTransaction();
PizzaOrder order = new PizzaOrder(size, roomNum,
adminDAO.findCurrentDay(), PizzaOrder.PREPARING, toppings);
pizzaOrderDAO.insertOrder(order);
dbDAO.commitTransaction();
} catch (Exception e) {
dbDAO.rollbackAfterException();
throw new ServiceException("Order can not be inserted ", e);
}
}
DbDAO: transaction Methods
The
transactions are delimited in the service layer, by calls to startTransaction
and commitTransaction or rollbackTransaction, as we see above.
We
are using Hibernate explicit transactions. Here getSession() returns the
Hibernate Session object.
See slides 55-59.
Because we are using ThreadLocalSessionContext (see hibernate.cfg.xml), commit or rollback closes the Session.
public void startTransaction() {
getSession().beginTransaction();
}
public void commitTransaction() {
getSession().getTransaction().commit(); // this closes the Session
}
public void rollbackTransaction() {
getSession().getTransaction().rollback();
}
// If the caller has already seen an exception, they probably
// don't want to handle a failing rollback, so it can use this.
// Then the caller should issue its own exception
public void rollbackAfterException() {
try {
rollbackTransaction();
} catch (Exception e) {
// discard secondary exception--probably server can't be reached
}
}
PizzaOrderDAO: Sample DAO methods showing Hibernate actions
public void insertOrder(PizzaOrder order)
{
dbDAO.getSession().persist(order);
}
Here is some HQL. See slides 41-44.
// Get orders for a certain day and room number
@SuppressWarnings("unchecked")
public Set<PizzaOrder> findOrdersByRoom(int roomNumber, int day)
{
List<PizzaOrder> orders = dbDAO.getSession().createQuery("from PizzaOrder o where o.roomNumber = "
+ roomNumber + " and o.day = " + day ).list();
return new TreeSet<PizzaOrder>(orders);
}
From PizzaSystemConfig.java: setting up the API objects
Instead of accepting username and password as arguments, configureServices gets them from hibernate.cfg.xml.
As in Pizza1, the lower-layer DAO
objects are created first and passed into the service layer object
constructors.
We end up with 2 Service layer and 3 DAO layer API objects, same as in Pizza1.
This is DI.
// configure service and DAO objects--called explicitly for client-server
public static void configureServices() { ßno longer needs user, password here
try {
HibernateUtil.initializeHibernate(); ß new: read in hibernate.cfg.xml
… some details left out…
DbDAO dbDAO = new DbDAO();
// configure rest of service and DAO singleton objects--
AdminDAO adminDAO = new AdminDAO(dbDAO);
PizzaOrderDAO pizzaOrderDAO = new PizzaOrderDAO(dbDAO);
adminService = new AdminService(dbDAO, adminDAO, pizzaOrderDAO);
studentService = new StudentService(dbDAO, pizzaOrderDAO, adminDAO);
} catch (Exception e) {
… //
report on Exception, its cause, to System.err
e.printStackTrace(); // also goes to System.err
// fatal error, already
reported, so make it a RuntimeException
throw new RuntimeException("Exception in
configureServices",e);
}
}
PizzaOrder.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated Oct 16, 2007 6:13:40 PM by Hibernate Tools 3.2.0.beta8 -->
<hibernate-mapping>
<class name="cs636.pizza.domain.PizzaOrder" table="PIZZA_ORDERS">
<id name="id" type="int">
<column name="ID" />
<generator class="native" />
</id>
<many-to-one name="pizzaSize" class="cs636.pizza.domain.PizzaSize" fetch="select">
<column name="SIZE_ID" not-null="true" />
</many-to-one>
<property name="roomNumber" type="int">
<column name="ROOM_NUMBER" not-null="true" />
</property>
<property name="day" type="int">
<column name="DAY" not-null="true" />
</property>
<property name="status" type="int">
<column name="STATUS" not-null="true" />
</property>
<set name="toppings" table="ORDER_TOPPING"> ßTelling Hibernate about
<key> the join table
<column name="ORDER_ID" not-null="true" />
</key>
<many-to-many entity-name="cs636.pizza.domain.Topping">
<column name="TOPPING_ID" not-null="true" />
</many-to-many>
</set>
</class>
</hibernate-mapping>
PizzaSize.hbm.xml
(Topping.hbm.xml is similar) Also
see slide 52. Since there is no mention of
PizzaOrder here, we see that the PizzaOrder-PizzaSize association is
unidirectional.
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated Oct 16, 2007 6:13:40 PM by Hibernate Tools 3.2.0.beta8 -->
<hibernate-mapping>
<class name="cs636.pizza.domain.PizzaSize" table="PIZZA_SIZE">
<id name="id" type="int">
<column name="ID" />
<generator class="native" />
</id>
<property name="sizeName" type="string">
<column name="SIZE_NAME" length="30" not-null="true" unique="true" />
</property>
</class>
</hibernate-mapping>
These were originally generated using hibernate tools, from the pizza database. Note that to get Hibernate to handle the join table for us, we need to have a <set> on at least one side.
--Oracle uses
"sequences" to generate new primary key values
--Hibernate uses this as the
native key generator
--(see <generator
class="native" /> in *.hbm)
--Specifically, Hibernate
looks for a sequence named
--hibernate_sequence by
default, so that's what we create here
create sequence
hibernate_sequence;
create table pizza_size(
id integer,
size_name varchar(30),
primary key (id),
unique (size_name));
create table pizza_orders(
id integer,
room_number integer,
size_id integer,
day integer,
status integer,
primary key(id),
foreign key (size_id) references pizza_size(id));
create table toppings(
id integer,
topping_name varchar(30),
primary key(id),
unique (topping_name));
create table order_topping (
order_id integer,
topping_id integer,
primary key (order_id,
topping_id),
foreign key (order_id) references pizza_orders (id),
foreign key (topping_id) references toppings(id));
create table pizza_sys_tab (
id int,
current_day integer,
last_report integer);
insert into pizza_sys_tab values (1, 1, 1);