Java 消息服务(Java Message Service,简称JMS)是企业级消息传递系统,紧密集成于Jboss Server 平台之中。
企业消息传递系统使得应用程序能够通过消息的交换与其他系统之间进行通信。
1、消息组成
消息传递系统的中心是消息。一条Message分为三个组成部分。
① 头:是个标准字段集,客户机和供应商都用它来标识和路由信息。
② 属性(property):支持把可选头字段添加到消息。如果您的应用程序需要不使用标准头字段对消息编目和分类,您就可以添加一个属性
到消息以实现这个编目和分类。提供set<Type>Property(...)和get<Type>Property(...)方法设置和获取各种Java类型的属性,包括Object。
③ 消息的主体:
2、消息的传递模式
点对点(PTP):一条消息只能传递给一个接收方;
发布/订阅(pub/sub):一条消息传递给多个接收方;
public class OrderItem implements Serializable {
@ManyToOne(cascade=CascadeType.REFRESH,optional=false)
@JoinColumn(name = "order_id")
public Order getOrder() {
return order;
}
//获取OrderItem时的SQL为:select * from OrderItem item inner join Orders o on o.order_id=item.id,
OrderItem表与orders表都必须有关联记录时,查询结果才有记录。
@ManyToOne(cascade=CascadeType.REFRESH,optional=true)
@JoinColumn(name = "order_id")
public Order getOrder() {
return order;
}
//获取OrderItem时的SQL为:select * from OrderItem item left outer join Orders o on o.order_id=item.id
如果orders表没有记录,OrderItem表有记录,查询结果仍有记录。
@JoinColumn(name = "order_id")注释指定 OrderItem 映射表的 order_id 列作为外键与 Order 映射表的主键列关联。
a、命名参数查询
//获取指定 personid 的人员
Query query = em.createQuery("select p from Person p where p.personid=:Id");
query.setParameter("Id",new Integer(1));
List result = query.getResultList();
b、位置参数查询
//获取指定 personid 的人员
Query query = em.createQuery("select p from Person p where p.personid=?1");
query.setParameter(1,new Integer(1));
List result = query.getResultList();
c、Date参数
如果你需要传递 java.util.Date 或 java.util.Calendar 参数进一个参数查询,你需要使用一个特殊的 setParameter()
方法,相关的 setParameter方法定义如下:
//命名参数查询时使用,参数类型为 java.util.Date
Query setParameter(String name, java.util.Date value, TemporalType temporalType);
//命名参数查询时使用,参数类型为 java.util.Calendar
Query setParameter(String name, Calendar value, TemporalType temporalType);
//位置参数查询时使用,参数类型为 java.util.Date
Query setParameter(int position, Date value, TemporalType temporalType);
//位置参数查询时使用,参数类型为 java.util.Calendar
Query setParameter(int position, Calendar value, TemporalType temporalType);
因为一个Date 或 Calendar对象能够描述一个真实的日期、 时间或时间戳.所以我们需要告诉Query对象怎么使用
这些参数,我们把 javax.persistence.TemporalType 作为参数传递进 setParameter方法,告诉查询接口在转换
java.util.Date 或 java.util.Calendar 参数到本地 SQL时使用什么数据库类型。
⑥ JPQL语言
Java Persistence API定义了一种查询语言,具有与SQL相类似的特征,JPQL是完全面向对象的,具备继承、多态和关联等特性。
a、大小写敏感性
除了Java 类和属性名称外,查询都是大小写不敏感的。所以,SeLeCT 和 sELEct 以及SELECT相同的,
但是 com.foshanshop.ejb3.bean.Person 和 com.foshanshop.ejb3.bean.PERSon 是不同的, person.name 和
person.NAME 也是不同的。
b、命名查询
在实体 bean 上预先定义一个或多个查询语句,减少每次因书写错误而引起的 BUG。通常把经常使用的查
询语句定义成命名查询,代码如下:
@NamedQuery(name="getPerson", query= "FROM Person WHERE personid=?1")
@Entity
@Table(name = "Person")
public class Person implements Serializable{
定义多个命名查询,应在@javax.persistence.NamedQueries注释里定义@NamedQuery,代码如下:
@NamedQueries({
@NamedQuery(name="getPerson", query= "FROM Person WHERE personid=?1"),
@NamedQuery(name="getPersonList", query= "FROM Person WHERE age>?1")
})
@Entity
@Table(name = "Person")
public class Person implements Serializable{
当命名查询定义好了之后,我们就可以通过名称执行其查询。代码如下:
Query query = em.createNamedQuery("getPerson");
query.setParameter(1, 1);
c、排序(order by)
EJB3 QL中默认为 asc 升序。
//先按年龄降序排序,然后按出生日期升序排序
Query query = em.createQuery("select p from Person p order by p.age desc, p.birthday asc");
和 SQL一样,如果聚合函数不是 select...from的唯一一个返回列,需要使用"GROUP BY"语句。"GROUP BY"应
该包含 select语句中除了聚合函数外的所有属性。
//返回男女生各自的总人数
Query query = em.createQuery("select p.sex, count(p) from Person p group by p.sex");
//集合中的元素不再是 Person,而是一个 Object[]对象数组
List result = query.getResultList();
StringBuffer out = new StringBuffer("*************** QueryGroupBy 结果打印*********<BR>");
if (result!=null){
Iterator iterator = result.iterator();
while( iterator.hasNext() ){
//取每一行
Object[] row = (Object[]) iterator.next();
//数组中的第一个值是 sex
boolean sex = Boolean.parseBoolean(row[0].toString());
//数组中的第二个值是聚合函数 COUNT 返回值
String sextotal = row[1].toString();
out.append((sex ? "男生":"女生")+ "总共有"+ sextotal+ "人<BR>");
}
}
return out.toString();
注意:如果还需要加上查询条件,需要使用"HAVING"条件语句而不是"WHERE"语句。
//返回人数超过 1 人的性别
Query query = em.createQuery("select p.sex, count(p) from Person p group by p.sex having count(*)>?1");
//设置查询中的参数
query.setParameter(1, new Long(1));
//集合中的元素不再是 Person,而是一个 Object[]对象数组
g、关联(join)
left out join/left join:left out join/left join 等,都是允许符合条件的右边表达式中的 Entiies为空。
inner join:inner join 要求右边的表达式必须返回 Entities。
left join/inner join fetch:left/left out/inner join fetch提供了一种灵活的查询加载方式来提高查询的性能。在默认的查询中,
Entity中的集合属性默认不会被关联,集合属性默认是缓加载( lazy-load )。
h、排除相同的记录 DISTINCT
Query query = em.createQuery("select DISTINCT o from Order o inner join fetch o.orderItems order by o.orderid");
i、比较 Entity
在查询中使用参数查询时,参数类型除了 String, 原始数据类型( int, double 等)和它们的对象类型( Integer, Double
等),也可以是 Entity的实例。
j、批量更新(Batch Update)
private String QueryBatchUpdate(){
//把所有订单的金额加 10
Query query = em.createQuery("update Order as o set o.amount=o.amount+10");
//update 的记录数
int result = query.executeUpdate();
k、批量删除(Batch Remove)
Query query = em.createQuery("delete from OrderItem item where item.order in(from Order as
o where o.amount<100)");
query.executeUpdate();
query = em.createQuery("delete from Order as o where o.amount<100");
int result = query.executeUpdate();//delete的记录数
l、使用操作符 NOT
//查询除了指定人之外的所有订单
Query query = em.createQuery("select o from Order o where not(o.ower =?1) order by o.orderid");
Person person = new Person();
person.setPersonid(new Integer(2));
//设置查询中的参数
query.setParameter(1,person);
List result = query.getResultList();
m、使用操作符 BETWEEN
//查询金额在 300 到 1000 之间的订单
Query query = em.createQuery("select o from Order as o where o.amount between 300 and 1000");
List result = query.getResultList();
n、使用操作符 IN
//查找年龄为 26,21 的 Person
Query query = em.createQuery("select p from Person as p where p.age in(26,21)");
List result = query.getResultList();
o、使用操作符 LIKE
//查找以字符串"li"开头的 Person
Query query = em.createQuery("select p from Person as p where p.name like ‘li%‘");
List result = query.getResultList();
//可以结合 NOT 一起使用,比如查询所有 name 不以字符串"ming"结尾的 Person
query = em.createQuery("select p from Person as p where p.name not like ‘%ming‘");
result = query.getResultList();
p、使用操作符 IS NULL
//查询含有购买者的所有 Order
Query query = em.createQuery("select o from Order as o where o.ower is not null order by
o.orderid");
List result = query.getResultList();
//查询没有购买者的所有 Order
query = em.createQuery("select o from Order as o where o.ower is null order by o.orderid");
result = query.getResultList();
q、使用操作符 IS EMPTY
IS EMPTY 是针对集合属性(Collection)的操作符。可以和 NOT 一起使用。低版权的 Mysql不支持 IS EMPTY。
//查询含有订单项的所有 Order
Query query = em.createQuery("select o from Order as o where o.orderItems is not empty order by
o.orderid");
List result = query.getResultList();
//查询没有订单项的所有 Order
query = em.createQuery("select o from Order as o where o.orderItems is empty order by o.orderid");
r、使用操作符 EXISTS
[NOT]EXISTS 需要和子查询配合使用。注:低版权的 Mysql不支持 EXISTS
//如果存在订单号为 1 的订单,就获取所有 OrderItem
Query query = em.createQuery("select oi from OrderItem as oi where exists (select o from Order o where o.orderid=1)");
//如果不存在订单号为 10 的订单,就获取 id 为 1 的 OrderItem
query = em.createQuery("select oi from OrderItem as oi where oi.id=1 and not exists (select o from
Order o where o.orderid=10)");
result = query.getResultList();
s、字符串函数
EJB3 QL定义了内置函数方便使用。这些函数的使用方法和 SQL中相应的函数方法类似。
符串函数包括:
1. CONCAT 字符串拼接
2. SUBSTRING 字符串截取
3. TRIM 去掉空格
4. LOWER 转换成小写
5. UPPER 装换成大写
6. LENGTH 字符串长度
7. LOCATE 字符串定位
//查询所有人员,并在姓名后面加上字符串"_foshan"
Query query = em.createQuery("select p.personid, concat(p.name, ‘_foshan‘) from Person as p");
List result = query.getResultList();
//查询所有人员,并在姓名后面加上字符串"_foshan"
Query query = em.createQuery("select p.personid, concat(p.name, ‘_foshan‘) from Person as p");
List result = query.getResultList();
t、计算函数
ABS 绝对值
SQRT 平方根
MOD 取余数
SIZE 取集合的数量
//查询所有 Order的订单号及其订单项的数量
Query query = em.createQuery("select o.orderid, size(o.orderItems) from Order as o group by o.orderid");
List result = query.getResultList();
//查询所有 Order的订单号及其总金额/10 的余数
query = em.createQuery("select o.orderid, mod(o.amount, 10) from Order as o");
result = query.getResultList();
u、子查询
子查询可以用于 WHERE和 HAVING 条件语句中。注:低版权的 Mysql不支持子查询。
//查询年龄为 26 岁的购买者的所有 Order
Query query = em.createQuery("select o from Order as o where o.ower in(select p from Person as p
where p.age =26) order by o.orderid");
List result = query.getResultList();
分***页v、结果集分页
QueryAPI有两个接口方法可以解决这个问题:setMaxResults( ) 和 setFirstResult( )
setMaxResults 方法设置获取多少条记录
setFirstResult 方法设置从结果集中的那个索引开始获取(假如返回的记录有3 条,容器会自动为记录编上索引,
索引从 0 开始,依次为 0,1,2)
public List getPersonList(int max,int whichpage) {
try {
int index = (whichpage-1) * max;
Query query = em.createQuery("from Person p order by personid asc");
List list = query.setMaxResults(max).setFirstResult(index).getResultList();
em.clear();//分离内存中受EntityManager管理的实体bean,让VM进行垃圾回收
return list;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
JSP 客户端调用代码片断:
<%@ page contentType="text/html; charset=GBK"%>
<%@ page import="com.foshanshop.ejb3.PersonDAO,
com.foshanshop.ejb3.bean.Person,
javax.naming.*,
java.util.Properties,
java.util.List,
java.util.Iterator"%>
<%
Properties props = new Properties();
props.setProperty("java.naming.factory.initial","org.jnp.interfaces.NamingContextFactory");
props.setProperty("java.naming.provider.url", "localhost:1099");
props.setProperty("java.naming.factory.url.pkgs", "org.jboss.naming");
InitialContext ctx = new InitialContext(props);
try {
PersonDAO persondao = (PersonDAO) ctx.lookup("PersonDAOBean/remote");
out.println("<br>============ 分页显示,每页记录数为2 =========<BR>");
String index = request.getParameter("index");
if (index==null || "".equals(index.trim())) index = "1";
int max = 2; //每页记录数为2
int whichpage = Integer.parseInt(index); //第几页
List list = persondao.getPersonList(max, whichpage);
if (list!=null){
Iterator it = list.iterator();
while (it.hasNext()) {
Person p = (Person)it.next();
out.println("人员编号:"+ p.getPersonid() + " 姓名:"+ p.getName() + "<Br>");
}
}
} catch (Exception e) {
out.println(e.getMessage());
}
%>
w、调用存储过程(使用MySql数据库)
要调用存储过程,我们可以通过 EntityManager 对象的 createNativeQuery()方法执行 SQL 语句(注意:这里说的是
SQL语句,不是 EJB3 QL), 调用存储过程的 SQL格式如下:
{call 存储过程名称(参数 1, 参数 2, … )}
在 EJB3 中你可以调用的存储过程有两种
1.无返回值的存储过程。
2.返回值为 ResultSet(以 select形式返回的值)的存储过程,EJB3 不能调用以 OUT 参数返回值的存储过程。
① 调用无返回值的存储过程
CREATE PROCEDURE `AddPerson`()
NOT DETERMINISTIC
SQL SECURITY DEFINER
COMMENT ‘‘
BEGIN
INSERT into person(`PersonName`,`sex`,`age`) values(‘存储过程‘,1,25);
END;
调用:
private String QueryNoneReturnValueStoreProcedure(){
//调用无返回参数的存储过程
Query query = em.createNativeQuery("{call AddPerson()}");
query.executeUpdate();
② 调用返回单值的存储过程
CREATE PROCEDURE `GetPersonName`(IN Pid INTEGER(11))
NOT DETERMINISTIC
SQL SECURITY DEFINER
COMMENT ‘‘
BEGIN
select personname from person where `personid`=Pid;
END;
调用:
//调用返回单个值的存储过程
Query query = em.createNativeQuery("{call GetPersonName(?)}");
query.setParameter(1, new Integer(1));
String result = query.getSingleResult().toString();
③ 调用返回表全部列的存储过程
CREATE PROCEDURE `GetPersonList`()
NOT DETERMINISTIC
SQL SECURITY DEFINER
COMMENT ‘‘
BEGIN
select * from person;
END;
调用:
//调用返回 Person 全部列的存储过程
Query query = em.createNativeQuery("{call GetPersonList()}", Person.class);
List result = query.getResultList();
④ 调用返回部分列的存储过程
CREATE PROCEDURE `GetPersonPartProperties`()
NOT DETERMINISTIC
SQL SECURITY DEFINER
COMMENT ‘‘
BEGIN
SELECT personid, personname from person;
END;
调用:
//调用返回部分列的存储过程
Query query = em.createNativeQuery("{call GetPersonPartProperties()}");
List result = query.getResultList();
StringBuffer out = new StringBuffer("*************** QueryPartColumnStoreProcedure 结果打印*********<BR>");
if (result!=null){
Iterator iterator = result.iterator();
while( iterator.hasNext() ){
//取每一行
Object[] row = ( Object[]) iterator.next();
//数组中的第一个值是 personid
int personid = Integer.parseInt(row[0].toString());
String PersonName = row[1].toString();
out.append("人员 ID="+ personid+ "; 姓名="+PersonName+ "<BR>");
⑦ 事务管理服务
当应用出现失败或异常时,它保证了数据库的完整性。你可以简单地将为一个POJO方法申明它的事务属性。
这样容器就可以在合适的上下文中运行这个方法。最常见的事务是定义在 session bean 的方法上,方法中
所有的数据库操作只有在方法正常退出时才会提交,如果方法抛出未捕获的异常,事务管理将回滚所有的变更。