Technology(Java EE 6 with Glassfish 3.1, Netbeans 7.0)
我有一个通过JPA访问数据库的应用程序客户端。不涉及EJB
。现在我需要为这个应用程序客户端添加一个web界面。所以我会选择使用JSF 2.x
。我对这里的设计有些担心,我希望社区能帮助我。因此,多亏了BalusC,我可以通过在persistence.xml中指定transaction-type=RESOURCE_LOCAL
,在独立的客户端应用程序中使用JPA
EDIT已根据BalusC的建议编辑了以下代码
这是我的应用程序客户端main
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("CoreInPU");
EntityManager em = emf.createEntityManager();
EntityDAO entityDAOClient = new EntityDAOClient(em);
Main pgm = new Main();
try {
process(entityDAOClient);
} catch (Exception e) {
logger.fatal("", e);
}finally{
em.close();
emf.close();
}
}
public void process(EntityDAO entityDAO){
validatePDF(List<pdfFiles>);
processPDF(List<pdfFiles>, entityDAO);
createPrintJob(List<pdfFiles>, entityDAO);
}
public void processPDF(List<pdfFiles>, EntityDAO entityDAO){
for(File file : pdfFiles){
entityDAO.create(file);
}
}
这是我的App Client中的DAO
接口类
public interface EntityDAO {
public <T> T create(T t);
public <T> T find(Class<T> type, Object id);
public List findWithNamedQuery(String queryName);
public List findWithNamedQuery(String queryName, int resultLimit);
}
这是应用程序客户端DAO
public class EntityDAOClient implements EntityDAO {
private EntityManager em;
private static Logger logger = Logger.getLogger(EntityDAOClient.class);
public EntityDAOClient(EntityManager em) {
this.em = em;
}
@Override
public <T> T create(T t){
em.getTransaction().begin();
em.persist(t);
em.getTransaction().commit();
return t;
}
@Override
public <T> T find(Class<T> type, Object id){
em.getTransaction().begin();
T t = em.find(type, id);
em.getTransaction().commit();
return t;
}
...
}
这是persistence.xml
<persistence-unit name="CoreInPU" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>com.wf.docsys.core.entity.Acknowledgement</class>
<class>com.wf.docsys.core.entity.PackageLog</class>
<properties>
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/core"/>
<property name="javax.persistence.jdbc.password" value="root"/>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.user" value="xxxx"/>
<property name="eclipselink.ddl-generation" value="create-tables"/>
</properties>
</persistence-unit>
现在我需要在此基础上添加一个web模块。我知道我需要JTA
事务类型,所以我创建了一个名为foo
的EAR项目,其中包含foo_ejb
和foo_war
。
@Stateless
@LocalBean
public class CoreEJB implements EntityDAO{
@PersistenceContext(unitName = "CoreInWeb-ejbPU")
private EntityManager em;
//@Override
public <T> T create(T t) {
em.persist(t);
return t;
}
//@Override
public <T> T find(Class<T> type, Object id) {
return em.find(type, id);
}
...
}
注意,CoreInWeb-ejbPU
是具有JTA
事务类型的新persistence.xml单元名称。我还将我的应用程序客户端jar文件添加到foo_ejb
包中当我部署时,我收到了这个消息Invalid ejb jar [foo-ejb.jar]: it contains zero ejb.
。这是因为这个@Stateless public class CoreEJB implements EntityDAO
。如果我取出implements EntityDAO
,那么它就被部署了,但我需要EJB实现EntityDAO
,这样我就可以在我的托管bean中执行这一操作
@ManagedBean
@RequestScoped
public class Bean {
@EJB
private CoreEJB coreEJB;
public Bean() {
}
public void runAppClientMainProcess() {
//The web interface can also kick off the same process as the app client
process(coreEJB);
}
// ...
}
如何正确执行此操作?请帮助
我知道我在这里可能要求太多了,但如果你能根据我上面的结构,向我展示如何在中添加网络模块,我将不胜感激。有些代码会很棒我还在学习,所以如果我的设计有缺陷,请随意撕毁它,如果我确信有更好的方法可以做到这一点,我会重新设计所有的东西。最重要的是,有一组业务逻辑,我想通过application client
和web interface
访问它们。就像glassfishv3有web界面和管理控制台一样
你们能告诉我应该把事务类型为"JTA"的另一个persistence.xml放在哪里吗?在我的web模块中,还是创建一个单独的EJB
这没什么不同。它仍然需要放在/META-INF/persistence.xml
文件中。如果您的项目代表一个WAR,请将其放在web项目中。或者,如果它代表一个EAR,就把它放在EJB项目中。
在JSF中,我使用托管Bean,我的托管Bean如何从EntityUtil调用方法?我问这个问题是因为通常我在某个地方有一个EJB,所以如果我想让我的托管bean访问它,我会使用@EJB注释注入EJB。由于EntityUtil没有被注释为EJB,我如何从ManagedBean访问它
在理论中,您只需创建一个新的EJB,它组成/委托EntityUtil
,然后将该EJB注入托管bean中。
@Stateless
public class SomeEJB {
@PersistenceUnit(unitName="someWebPU")
private EntityManagerFactory emf;
@PostConstruct
public void init() {
EntityUtil.newInstance(emf);
}
public void create(Some some) {
EntityUtil.create(some);
}
// ...
}
然而。。。您的EntityUtil
不是线程安全的。EntityUtil
中的所有内容都是static
。Java EE web应用程序是一个高度多线程的环境。多个并发用户同时使用相同的代码库。所有公开的静态变量都在所有用户之间共享。当用户在您的EntityUtil
上调用close()
时,它将影响网络应用程序的所有当前用户。因此,那些忙于事务的人会得到一个异常。
无论是客户端还是web应用程序,都应该在应用程序启动时只创建一次EntityManagerFactory
,在应用程序的整个生命周期中重用同一实例,并仅在应用程序关闭时关闭它。每个事务或会话只需要创建一次EntityManager
,并在事务或会话结束时关闭。在具有JTA事务类型的JavaEE6中,事务完全由容器管理。但在具有资源本地事务类型的客户端应用程序中,您需要自己管理事务。
我建议将EntityUtil
重写为类似DAO的接口,该接口旨在为客户端应用程序和web应用程序提供不同的实现。
public interface SomeDAO {
public void save(Some some);
// ...
}
以下是如何为客户端应用程序实现它:
public class SomeDAOClient implements SomeDAO {
private EntityManager em;
public SomeDAO(EntityManager em) {
this.em = em;
}
public void save(Some some) {
em.getTransaction().begin();
em.persist(some);
em.getTransaction().commit();
}
// ...
}
并按如下方式使用:
public static void main(String[] args) throws Exception {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("someClientPU");
EntityManager em = emf.createEntityManager();
SomeDAO someDAO = new SomeDAOClient(em);
try {
Some some = new Some();
some.setFoo("foo");
someDAO.save(some);
} finally {
em.close();
emf.close();
}
}
以下是如何在web应用程序中实现它:
@Stateless
public class SomeDAOEJB implements SomeDAO {
@PersistenceContext(unitName="someWebPU")
private EntityManager em;
public void save(Some some) {
em.persist(some);
}
// ...
}
并按以下使用
@ManagedBean
@RequestScoped
public class Bean {
@EJB
private SomeDAO someDAO;
private Some some;
public Bean() {
some = new Some();
}
public void save() {
someDAO.save(some);
}
// ...
}
@哈利您正在讨论三个层次1)演示2)业务3)持久性
案例1-应用程序客户端-您使用了用于演示的主类,validatePDF、processPDF、createPrintJob是您的业务层3)EntityDAOClient,JPA是您的持久层
案例2-Web模块-您已经使用(希望使用)jsf进行表示,使用CoreEJB进行持久性--我不清楚您打算将业务逻辑放在哪里,现在我将其视为另一个ejb类。
这种方法的主要问题是"对业务逻辑层使用两种不同的方法"案例1——纯java案例2 Ejb。
当你使用纯java时,你就失去了依赖注入、tx管理、并发管理、拦截器等的奢侈
当您使用EJB时,您失去了与应用程序客户端共享代码的灵活性。
解决方案是使用CDI。使用CDI,您可以集成这两个用例。它就像三层之间的桥梁。将依赖注入、拦截器、装饰器的所有功能都提供给应用程序客户端。
实际上,应用程序客户端和JavaEE中的业务层和持久层代码将不会发生任何更改。
我在CDI编程模型中提到过你不需要使用@managed吗。所有的pojo都是CDI豆。
在CDI中,您不需要编写一个主类。开始编写如下所示的业务逻辑
//This class works as is in both javaEE and client.
//There is no need of managedbean in CDI.
//This class can be accessed in jsf using EL #businessProcessor
@Singleton @Named
public class BusinessProcessor
{
private @Inject EntityDAO entityDAO; // CDI will inject an implementation of EntityDao.
//In our case there is only one impl for client as well as JavaEE
// CDI will invoke below method on startup. Not used in JavaEE.
// In JavaEE JSF will execute process() directly using Expression Language
public void init(@Observes ContainerInitialized event, @Parameters List parameters){
process(entityDAO);
}
public void process(@Inject EntityDAO entityDAO){
validatePDF(List);
processPDF(List, entityDAO);
createPrintJob(List, entityDAO);
}
public void processPDF(List, EntityDAO entityDAO){
for(File file : pdfFiles){
entityDAO.create(file);
}
}
}
// this class is same for both JavaEE and Client
public class EntityDAOClient implements EntityDAO {
private @Inject EntityManager em;
private static Logger logger = Logger.getLogger(EntityDAOClient.class);
@Override
public T create(T t){
em.getTransaction().begin();
em.persist(t);
em.getTransaction().commit();
return t;
}
@Override
public T find(Class type, Object id){
em.getTransaction().begin();
T t = em.find(type, id);
em.getTransaction().commit();
return t;
}
...
}
To run the app client
java org.jboss.weld.environment.se.StartMain
For more meat check out www.seamframework.orgyou can do some little project examples with netbeans, and see how it works.. NetBeans generate all these "sad" things for you, so, you can study it, and do it by yourself.
I want to apologize for my late reply. I was behind the schedule for another project so I do not have time to write a proper response to BalusC
and kiran
answers.
kiran
ask me this: Case 2 - Web Module - You have used(want to use) jsf for presentation , CoreEJB for persistence -- i am not clear where you intend to put business logic, for now ill take is as another ejb class.
Answer: Hi kiran, the bottom line is that, I want the same set of logic to be access at both app client side and web module side. Like glassfish app server. You have a command line version and web module version accessing the same resources.
BalusC
suggestion to use RESOURCE_LOCAL
work great if I just use application client. However, once I adding the web module on top of it, I cant not get it to work anymore even though BalusC did try to work with me. You guys can read BalusC response and my implementation of his idea above (The code from the original post is my implementation of BalusC idea). If I add the App Client to the Java EE project (EAR) classpath, it receive no compile error when I try to access the logic from App Client, but when i run the web module, it generate Exception saying that it does not see the class/method that I try to access from App Client. So I decide to port all my logic onto EJB and make my App Client very thin. The logics will be exposed to App Client via javax.ejb.Remote
interface, and exposed to the Web module via javax.ejb.Local
interface. So below is my general layout of this idea.
This is my thin
App Client (Main)
public class Main {
@EJB
private static CoreMainEJBRemote coreEJBRemote;
private static Logger logger = Logger.getLogger(Main.class);
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
coreEJBRemote.process(args[0]);
}
}
因此,在我的EJB方面,Java EE项目(EAR)中包含EJB和web模块的哪个包,我有
@Stateless
public class CoreMainEJB implements CoreMainEJBRemote, CoreMainEJBLocal {
@EJB
private PackageProcessor packageProcessor;
@Override
public void process(String configFileName) {
...
//Process config File
packageProcessor.validatePDF();
packageProcessor.processPDF();
...
}
}
注意,由于所有逻辑现在都在EJB中,所以我可以使用JTA
事务类型,它是容器管理的。我更喜欢这种方式,而不是自己管理。
有人建议通过RESTful公开业务逻辑。由于我对RESTful不是很了解,所以现在我将继续使用这个实现。感谢您对BalusC
和kiran
的帮助。