findAllCustomers() 方法 该方法调用Session 的 find() 方法,查询所有的 Customer 对象。 tx = session.beginTransaction(); List customers=session.find("from Customer as c order by c.name asc"); for (Iterator it = customers.iterator(); it.hasNext();) { printCustomer(context,out,(Customer) it.next()); } tx.commit(); Session 的 find() 方法有好几种重载形式,本例中传递的是字符串参数 “ from Customer as c order by c.name asc” ,它使用的是 Hibernate 查询语言。运行 session.find() 方法时, Hibernate 执行以下 SQL 语句: select * from CUSTOMERS order by NAME asc;
在 Customer 类中关联Order 类 public class Customer implements Serializable{ ...... private Set orders=new HashSet(); public Set getOrders(){ return orders; } public void setOrders() { this.orders=orders; } }
运行 Session 的find() 方法 List customerLists=session.find("from Customer as c"); 运行以上 find() 方法时, Hibernate 将先查询 CUSTOMERS 表中所有的记录,然后根据每条记录的 ID ,到 ORDERS 表中查询有参照关系的记录, Hibernate 将依次执行以下 select 语句: select * from CUSTOMERS; select * from ORDERS where CUSTOMER_ID=1; select * from ORDERS where CUSTOMER_ID=2; select * from ORDERS where CUSTOMER_ID=3; select * from ORDERS where CUSTOMER_ID=4;
在程序中显式指定迫切左外连接检索策略 以下 Session的 find() 方法都用于检索 OID 为 1 的 Customer 对象: session.find("from Customer as c where c.id=1"); session.find("from Customer as c left join fetch c.orders where c.id=1"); 在执行第一个 find() 方法时,将使用映射文件配置的检索策略。在执行第二个 find() 方法时,在 HQL 语句中显式指定迫切左外连接检索关联的 Order 对象,因此会覆盖映射文件配置的检索策略。不管在 Customer.hbm.xml 文件中 <set> 元素的 lazy 属性是 true 还是 false , Hibernate 都会执行以下 select 语句: select * from CUSTOMERS left outer join ORDERS on CUSTOMERS.ID =ORDERS.CUSTOMER_ID where CUSTOMERS.ID=1;
分页查询 // 采用 HQL 检索方式 Query query = session.createQuery("from Customer c order by c.name asc"); query.setFirstResult(0); query.setMaxResults(10); List result = query.list(); // 采用 QBC 检索方式 Criteria criteria = session.createCriteria( Customer.class); criteria.addOrder( Order.asc("name") ); criteria.setFirstResult(0); criteria.setMaxResults(10); List result = criteria.list();
135.
迫切左外连接 以下两种检索方式是等价的,它们都能同时迫切左外连接类B 和类 C : //HQL 迫切左外连接检索方式 from A a left join fetch a.b b left join fetch a.c c where b is not null and c is not null //QBC 迫切左外连接检索方式 List result=session.createCriteria(A.class) .setFetchMode("this.b",FetchMode.EAGER) .setFetchMode("this.c",FetchMode.EAGER) .add(Expression.isNotNull("this.b")) .add(Expression.isNotNull("this.c")) .list(); 迫切左外连接
136.
投影查询 select 关键字用于选择对象的部分属性,例如: Iterator it=session.createQuery( " select c.id,c.name,o.orderNumber " + " from Customer c join c.orders o " +" where o.orderNumber like 'T%'" ).list().iterator(); while(it.hasNext()){ Object[] row=(Object[])it.next(); Long id=(Long)row[0]; String name=(String)row[1]; String orderNumber=(String)row[2]; System.out.println(id+" "+name+" "+orderNumber); }
137.
投影查询 HQL 查询语句对应的SQL 语句为: select c.ID,c.NAME,o.ORDER_NUMBER from CUSTOMERS c inner join ORDERS o on c.ID=o.CUSTOMER_ID where o.ORDER_NUMBER like 'T%'; 以上查询语句的查询结果如下: +----+------+--------------+ | ID | NAME | ORDER_NUMBER | +----+------+--------------+ | 1 | Tom | Tom_Order001 | | 1 | Tom | Tom_Order002 | | 1 | Tom | Tom_Order003 | +----+------+--------------+ Query 的 list() 方法返回的集合中包含三个对象数组类型的元素,每个对象数组代表以上查询结果的一条记录。
LockMode NONE No lock required. READ A shared lock. * UPGRADE An upgrade lock. UPGRADE_NOWAIT Attempt to obtain an upgrade lock, using an Oracle-style select for update nowait. LockMode WRITE A WRITE lock is obtained when an object is updated or inserted.** * 读数据自动获得,绕过隔离级别和缓存,使用版本检查 ** 写数据自动获得,内部模式,程序中部可以明确指定此模式
CUSTOMERS 表参照 ADDRESS表 建立关系数据模型的一个重要原则是在不会导致数据冗余的前提下,尽可能减少数据库表的数目以及表之间的外键参照关系。因为如果表之间的外键参照关系很复杂,那么数据库系统在每次对关系数据进行插入、更新、删除和查询等 SQL 操作时,都必须建立多个表的连接,这是很耗时的操作,会影响数据库的运行性能。 以下 SQL 语句用于查询名为 “ Tom” 的客户的家庭地址: select PROVINCE,CITY,STREET,ZIPCODE from CUSTOMERS as c, ADDRESS as a where c.HOME_ADDRESS_ID =a.ID and c.NAME= 'Tom';
182.
粗粒度关系数据模型 create table CUSTOMERS ( ID bigint not null, NAME varchar(15), HOME_STREET varchar(255), HOME_CITY varchar(255), HOME_PROVINCE varchar(255), HOME_ZIPCODE varchar(255), COM_STREET varchar(255), COM_CITY varchar(255), COM_PROVINCE varchar(255), COM_ZIPCODE varchar(255), primary key (ID));
区分值( Value )类型和实体(Entity )类型 Hibernate 把持久化类的属性分为两种:值( Value )类型和实体( Entity )类型。 值类型和实体类型的最重要的区别是前者没有 OID ,不能被单独持久化,它的生命周期依赖于所属的持久化类的对象的生命周期,组件类型就是一种值类型;而实体类型有 OID ,可以被单独持久化。
191.
区分值( Value )类型和实体(Entity )类型 假定 Customer 类有以下属性: private String name; private int age; private Date birthday; // Customer 和 Address 类是组成关系 private Address homeAddress; private Address comAddress; //Customer 和 Company 类是多对一关联关系 private Company currentCompany; //Customer 和 Order 类是一对多关联关系 private Set orders; 在以上属性中, name 、 age 、 birthday 、 homeAddress 以及 comAddress 都是值类型属性,而 currentCompany 是实体类型属性, orders 集合中的 Order 对象也是实体类型属性。当删除一个 Customer 持久化对象时, Hibernate 会从数据库中删除所有值类型属性对应的数据,但是实体类型属性对应的数据有可能依然保留在数据库中,也有可能被删除,这取决于是否在映射文件中设置了级联删除。假如对 orders 集合设置了级联删除,那么删除 Customer 对象时,也会删除 orders 集合中的所有 Order 对象。假如没有对 currentCompany 属性设置级联删除,那么删除一个 Customer 对象时, currentCompany 属性引用的 Company 对象依然存在。
业务代理接口 INetstoreService public interface INetstoreService extends IAuthentication { /** 批量检索 Item 对象, beginIndex 参数指定查询结果的起始位置, length 指定检索的 Item 对象的数目。对于 Item 对象的所有集合属性,都使用延迟检索策略 */ public List getItems(int beginIndex,int length) throws DatastoreException; /** 根据 id 加载 Item 对象 */ public Item getItemById( Long id ) throws DatastoreException; /** 根据 id 加载 Customer 对象,对于 Customer 对象的 orders 属性,显式采用迫切左外连接检 索策略 */ public Customer getCustomerById( Long id ) throws DatastoreException; /** 保存或者更新 Customer 对象,并且级联保存或更新它的 orders 集合中的 Order 对象 */ public void saveOrUpdateCustomer(Customer customer ) throws DatastoreException; /** 保存订单 */ public void saveOrder(Order order) throws DatastoreException; public void setServletContext( ServletContext ctx ); public void destroy(); }
198.
IAuthentication 接口 public interface IAuthentication { /** 登出 Web 应用 */ public void logout(String email); /** 根据客户的 email 和 password 验证身份,如果验证成功,返回 匹配的 Customer 对象,它的 orders 集合属性采用延迟检索策略, 不会被初始化 */ public Customer authenticate(String email, String password) throws InvalidLoginException, ExpiredPasswordException,AccountLockedException, DatastoreException; }
199.
NetstoreServiceImpl 的 authenticate()方法 tx = session.beginTransaction(); // 对与 Customer 关联的 Order 对象采用延迟检索策略 Query query = session.createQuery("from Customer c where c.email=:email and c.password=:password"); query.setString("email",email); query.setString("password",password); List result = query.list(); tx.commit(); if(result.isEmpty()) throw new InvalidLoginException(); return (Customer)result.iterator().next();
200.
NetstoreServiceImpl 的 getCustomerById()方法 tx = session.beginTransaction(); // 对与 Customer 关联的 Order 对象采用迫切左外连接检索策略 Query query = session.createQuery("from Customer c left outer join fetch c.orders where c.id=:id"); query.setLong("id",id.longValue()); Customer customer =(Customer) query.uniqueResult(); tx.commit(); return customer;
INetstore 接口 publicinterface INetstore { public Customer authenticate(String email, String password) throws InvalidLoginException, ExpiredPasswordException,AccountLockedException,DatastoreException, RemoteException; public List getItems(int beginIndex,int length) throws DatastoreException, RemoteException; public Item getItemById( Long id ) throws DatastoreException, RemoteException; public Customer getCustomerById( Long id ) throws DatastoreException, RemoteException; public void saveOrUpdateCustomer(Customer customer ) throws DatastoreException, RemoteException; public void saveOrder(Order order) throws DatastoreException, RemoteException; public void destroy( ) throws RemoteException; }
216.
Remote 接口: NetstoreEJB接口 import javax.ejb.EJBObject; public interface NetstoreEJB extends EJBObject, INetstore { /** * The remote interface for the Netstore session bean. All methods are * declared in the INetstore business interface. */ }
217.
Home 接口: NetstoreEJBHome 接口 Home 接口定义了创建、查找和删除 EJB 的方法。本例中的 NetstoreEJBHome 接口包含了一个 create() 方法,这个方法返回一个 NetstoreEJB 对象的远程引用。 import java.rmi.RemoteException; import javax.ejb.CreateException; import javax.ejb.EJBHome; /** * The home interface for the Netstore session bean. */ public interface NetstoreEJBHome extends EJBHome { public NetstoreEJB create() throws CreateException, RemoteException; }
创建 EJBHomeFactory 通过JNDI 查找 HOME 接口是一项非常耗时的工作。如果对于每个需要访问 EJB 组件的客户请求,都先执行查找 HOME 接口的步骤,那么显然会降低 Web 应用的运行效率。事实上, HOME 接口是无状态的,不和特定的客户关联,因此同一个 HOME 接口引用可以被多个客户请求或客户线层共享。 为了提高访问 EJB 组件的效率,可以把已经获得的 HOME 接口引用保存在 Web 应用的缓存中,避免以后重复查找相同的 HOME 接口。保存 HOME 接口引用有两种方式: 一种方式是把 HOME 接口引用存放在 ServletContext 中; 还有一种方式是运用 EJBHomeFactory 模式,把 HOME 接口引用存放在专门的 EJB HOME 工厂类中。 第二种方式不依赖于 ServletContext 类,对于不是基于 Web 的应用程序也同样适用。
222.
创建 EJBHomeFactory publicclass EJBHomeFactory { private Map homes; private static EJBHomeFactory singleton; private InitialContext ctx; private EJBHomeFactory() throws NamingException { homes = Collections.synchronizedMap(new HashMap()); ctx = new InitialContext(); } public static EJBHomeFactory getInstance() throws NamingException { if (singleton == null) { singleton = new EJBHomeFactory(); } return singleton; } public EJBHome lookupHome(String jndiName, Class homeClass) throws NamingException { EJBHome home = (EJBHome)homes.get(homeClass); if (home == null) { home = (EJBHome)PortableRemoteObject.narrow(ctx.lookup( jndiName), homeClass); // Cache the home for repeated use homes.put(homeClass, home); } return home; } } EJBHomeFactory 类包含一个 Map 类型的 homes 成员变量,它充当 HOME 接口引用的缓存。 EJBHomeFactory 类的 lookupHome() 方法先查看是否在 homes 缓存中存放了所要查找的 HOME 接口引用,如果存在,就直接返回这个接口引用;如果不存在,就通过 JNDI API 查找 HOME 接口引用,把找到的接口引用保存在 homes 缓存中,并返回这个接口引用。
223.
业务代理实现类 NetstoreEJBFromFactoryDelegate 在 init() 方法中,从 EJB HOME 工厂中获得 HOME 接口引用 ,然后获得 NetstoreEJB 的远程引用 public NetstoreEJBFromFactoryDelegate(){ init(); } private void init(){ try { NetstoreEJBHome home = (NetstoreEJBHome)EJBHomeFactory.getInstance(). lookupHome("java:comp/env/ejb/NetstoreEJB", NetstoreEJBHome.class); netstore = home.create(); } ……
给 Web 应用打包在发布 Web 应用时,应该把它打包为 WAR 文件。假定 netstore 应用的所有源文件位于 <netstore> 目录下。在 DOS 窗口中,转到 <netstore> 目录,运行如下命令: jar cvf netstore.war *.* 在 <netstore> 目录下将生成 netstore.war 文件。如果希望单独发布这个 Web 应用,只要把这个 WAR 文件拷贝到 <JBOSS_HOME>\server\default\deploy 下即可。