jboss quickStart创建的项目,从index页面开始学习。整理学习遇到的问题
实现目标:
实现member的新增和数据库表的显示
每新增一条记录自动刷新列表
一、自己要实现的页面:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
>
<h:body>
<br /> 新增
<h:form>
<h:panelGrid columns="2">
<h:outputLabel value="name" for="name"/>
<h:inputText value="#{myNewMember.name}" id="name"/>
<h:outputLabel value="email" for="email"/>
<h:inputText value="#{myNewMember.email}" id="email"/>
<h:outputLabel value="phoneNumber" for="phoneNumber"/>
<h:inputText value="#{myNewMember.phoneNumber}" id="phoneNumber"/>
<h:commandButton action="#{myController.myRegister()}" value="register" />
</h:panelGrid>
</h:form>
<br /> 表格
<ui:remove>
<p:dataList value="#{memberController.memberList()}" var="_member" >
<f:facet name="header">
Member
</f:facet>
<p:panel header="#{_member.id}">
<h:outputText value="#{_member.email}" class="removeBtn"/>
</p:panel>
</p:dataList>
</ui:remove>
<p:dataList value="#{myMembers}" var="_member" >
<f:facet name="header">
Member
</f:facet>
<p:panel header="#{_member.name}">
<h:outputText value="#{_member.email}" class="removeBtn"/>
</p:panel>
</p:dataList>
</h:body>
</html>
<?xml version="1.0" encoding="UTF-8"?> 页面编码格式,不然传到后台的数据格式乱码!
#{memberController.memberList()} 是在model里写的静态的查询数据表信息,无法接收event事件实现自动刷新。代码如下
package org.jboss.tools.examples.controller;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.Model;
import javax.enterprise.inject.Produces;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import org.jboss.tools.examples.model.Member;
import org.jboss.tools.examples.service.MemberRegistration;
// The @Model stereotype is a convenience mechanism to make this a request-scoped bean that has an
// EL name
// Read more about the @Model stereotype in this FAQ:
// http://www.cdi-spec.org/faq/#accordion6
@Model
public class MemberController {
@Inject
private EntityManager em;
//写个查询list
public List<Member> memberList(){
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<Member> query = criteriaBuilder.createQuery(Member.class); //createQuery的参数是Query返回值类型
Root<Member> member = query.from(Member.class); //参数是对应于order表的实体类,query.from类似于sql中的from语句,该方法的执行等价于sql中的from Member。
//默认查询全部
query.select(member); //query.select(root)则等价于select *
TypedQuery<Member> tq = em.createQuery(query);
List<Member> list = tq.getResultList();
return list;
}
}
二、显示 myMembers
1、后台代码如下,这里通过打印语句把构造函数和 @PostConstruct 注解进行了比较,代码执行顺序。构造函数先执行,@PostConstruct再执行一次。
2、主要就是查询 myMembers 列表,类是@RequestScoped,每次request执行一次class(例如一次action)
3、CDI事件
知识点只有两个:
1 @Observes注解
2 Event<T>接口
正确的做法是写一个观察者类,在观察者类中的观察方法参数里加上@Observes注解就可以了。
参考例子:CDI进阶第五步 CDI事件
当前例子中 @Observes 新增完以后 会通知观察者 重新获取数据 实现当前页面的刷新
reGetMemberList方法再一次被执行,myMembers重新获取新值,实现页面刷新。
4、特别注意 @Produces
@Named 的位置
二者缺一不可,缺少一个,html就定位不到该bean方法。
@Produces 动态注入,标识生产者方法或字段。 可以应用于bean类的方法或字段。
@Named 基于字符串的限定符。
@Named注释的主要作用是定义一个bean,用于解析应用程序中的EL语句,通常是通过JSF EL解析器。
package org.jboss.tools.examples.data;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.Produces;
import javax.inject.Inject;
import javax.inject.Named;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import org.jboss.tools.examples.model.Member;
@RequestScoped
public class MyMemberListProducer {
//构造函数
public MyMemberListProducer() {
System.out.println("MyMemberListProducer 构造函数");
}
private List<Member> myMembers;
@Inject
private EntityManager em;
@Produces
@Named
public List<Member> getMyMembers() {
return myMembers;
}
//新增完以后 会通知观察者 重新获取数据 实现当前页面的刷新
public void reGetMemberList(@Observes Member member) {
System.out.println("@Observes:"+member.getName());
getMemberList();
}
//构造函数以后执行这里
@PostConstruct
public void getMemberList() {
System.out.println("MyMemberListProducer @PostConstruct");
myMembers = memberList();
}
public List<Member> memberList() {
System.out.println("开始查询数据库...");
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<Member> query = criteriaBuilder.createQuery(Member.class); // createQuery的参数是Query返回值类型
Root<Member> member = query.from(Member.class); // 参数是对应于order表的实体类,query.from类似于sql中的from语句,该方法的执行等价于sql中的from
// 默认查询全部
query.select(member); // query.select(root)则等价于select *
// 条件查询
// Predicate condition = criteriaBuilder.equal(member.get("name"), "王灵生");
// query.where(condition);
TypedQuery<Member> tq = em.createQuery(query);
List<Member> list = tq.getResultList();
return list;
}
}
5、页面第一次加载
三、新增myNewMember
1、这里使用 @Produces //动态注入
@Named 注解 myNewMember
2、@PostConstruct 第一次就执行 完成初始化
3、以为涉及到 persist操作,需要用到事物。单独在model里写的话会报错,需要添加@Transactional注解。
根据官方Demo学习,写了一个方法myRegister,使用@stateless 注解(这里的解释是 @Stateless注释消除了手动事务划分的需要)
解释:
@stateless 是EJB的bean,EJB不需要配置任何注解就自带了事物,所以这里添加了 @Stateless就不用添加@Transactional注解了。CDI与EJB不同的是默认不带事物上下文的,要是直接在@Model里面写数据库操作的话就会报错!
4、fire涉及到CDI的 event
这个代码里注入了一个事件对象。在业务方法中将调用事件对象的绑定方法。然后CDI上下文就会去找观察者。
也可以参考以下链接学习:
package org.jboss.tools.examples.controller;
import javax.annotation.PostConstruct;
import javax.enterprise.event.Event;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.Model;
import javax.enterprise.inject.Produces;
import javax.inject.Inject;
import javax.inject.Named;
import javax.persistence.EntityManager;
import org.jboss.tools.examples.model.Member;
import org.jboss.tools.examples.service.MyMemberRegistration;
@Model
public class MyController {
@Inject
private MyMemberRegistration myMemberRegistration;
@Produces
@Named
private Member myNewMember;
//注解,初始化执行一次
@PostConstruct
public void initMyMember() {
myNewMember = new Member();
}
public void myRegister() throws Exception{
myMemberRegistration.myRegister(myNewMember);
//清空 界面
initMyMember();
}
}
//The @Stateless annotation eliminates the need for manual transaction demarcation
//@Stateless注释消除了手动事务划分的需要
@Stateless
public class MyMemberRegistration {
@Inject
private EntityManager em;
@Inject
private Event<Member> event;
public void myRegister(Member member) {
em.persist(member);
//通知观察者 执行带 @Observes 的方法 (重新获取数据库中的member)
//使用指定的限定符触发事件并通知观察者。
event.fire(member);
}
四、测试
新增点击register按钮就会写入数据库一条新纪录,下面的列表自动更新。
这样就完成了一个最基本简单的新增查询操作,涉及到的注解特别多,理解方法的执行顺序特别关键。
还学习到了观察者模式,想起了以前开发android用到的EventBus,哦,之前是传参,好像不太一样!