Technology(Java EE 6 with Glassfish 3.1, Netbeans 7.0)
我有一个通过JPA访问数据库的应用程序客户端。不涉及EJB
。现在我需要为此应用程序客户端添加Web界面。所以我会选择使用JSF 2.x
。我对这里的设计有些担心,我希望社区能帮助我。 So thanks to BalusC, I am able to use JPA in a stand alone client application通过在persistence.xml中指定transaction-type=RESOURCE_LOCAL
。以下是演示:
编辑以下代码已根据BalusC建议进行了编辑
以下是我的 App Client 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);
}
以下是 App Client 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
事务类型,因此我创建了一个EAR项目调用foo
,其中包含foo_ejb
和foo_war
。所以我的EJB看起来像这样。
@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单元名称。我还将我的app客户端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);
}
// ...
}
如何正确执行此操作?请帮助
我知道我可能会在这里问太多,但如果您可以根据我上面的结构,告诉我如何添加一个Web模块,我将非常感激。有些代码会很棒。 我还在学习,所以如果我的设计有缺陷,请随意撕开它,如果我说服有更好的方法来实现这一点,我会重新设计一切。最重要的是,有一组业务逻辑,我想通过application client
和web interface
访问这些逻辑。就像glassfishv3有web界面和管理控制台一样
答案 0 :(得分:6)
你能告诉我在哪里可以使用transaction-type =“JTA”放置/创建另一个persistence.xml吗?在我的Web模块中,或者创建一个单独的EJB?
没有什么不同。它仍然需要进入/META-INF/persistence.xml
文件。如果您的项目代表WAR,请将其放在Web项目中。或者,如果它代表EAR,则将其放在EJB项目中。
在JSF中,我使用Managed Bean,我的托管bean如何从EntityUtil调用方法?我问这个问题,因为通常我在某处有一个EJB,所以如果我希望我的Managed bean访问它,我会使用@EJB注释注入EJB。由于EntityUtil未注释为EJB,我如何从Managed Bean访问它?
在理论中,您可以创建一个新的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应用程序是一个严重多线程的环境。多个并发用户同时使用相同的代码库。所有公开暴露的静态变量都在所有用户之间共享。当用户在close()
上调用EntityUtil
时,它会影响所有当前webapp用户。因此,忙于交易的人将获得例外。
无论是客户端还是Web应用程序,您都应该在应用程序启动时仅创建EntityManagerFactory
一次,在整个应用程序的生命周期内重用相同的实例,并仅在应用程序关闭时关闭它。每个事务或会话只需创建一次EntityManager
,并在事务或会话结束时关闭EntityUtil
。在具有JTA事务类型的Java EE 6中,事务由容器完全管理。但是在具有资源本地事务类型的客户端应用程序中,您需要自己管理事务。
我建议您将public interface SomeDAO {
public void save(Some some);
// ...
}
重写为类似DAO的界面,该界面旨在为客户端应用和网络应用提供不同的实现。
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();
}
}
并按如下方式使用:
@Stateless
public class SomeDAOEJB implements SomeDAO {
@PersistenceContext(unitName="someWebPU")
private EntityManager em;
public void save(Some some) {
em.persist(some);
}
// ...
}
以下是如何为Web应用程序实现它:
@ManagedBean
@RequestScoped
public class Bean {
@EJB
private SomeDAO someDAO;
private Some some;
public Bean() {
some = new Some();
}
public void save() {
someDAO.save(some);
}
// ...
}
并按如下方式使用
{{1}}
答案 1 :(得分:2)
@Harry 您正在讨论三个层次1)演示文稿2)业务3)持久性
案例1 - 应用程序客户端 - 您已使用主类进行演示,validatePDF,processPDF,createPrintJob是您的业务层3)EntityDAOClient和JPA是您的持久层
案例2 - Web模块 - 您已经使用(想要使用)jsf进行演示,使用CoreEJB进行持久化 - 我不清楚您打算放置业务逻辑的位置,因为现在生病是另一个ejb类。
这种方法的主要问题是“为业务逻辑层使用两种不同的方法”案例1 - 普通的Java Case 2 Ejb。
当你使用普通的java时,你失去了依赖注入,tx管理,并发管理,拦截器等的奢侈。
当您使用EJB时 - 您失去了与应用程序客户端共享代码的灵活性。
解决方案是使用CDI。使用CDI,您可以集成两个用例。它就像三层之间的桥梁。将依赖注入,拦截器,装饰器的所有功能提供给应用程序客户端。
实际上,应用程序客户端和JavaEE中的业务层和持久层代码都不会发生任何变化。
我在CDI编程模型中提到你不需要使用@managed。所有的pojo都是CDI bean。
在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;
}
...
}
运行应用客户端
java org.jboss.weld.environment.se.StartMain
如需更多肉类,请查看www.seamframework.org
答案 2 :(得分:0)
您可以使用netbeans做一些小项目示例,看看它是如何工作的.. NetBeans为您生成所有这些“悲伤”的东西,因此,您可以自己研究它,并自己完成。
答案 3 :(得分:0)
我想为我迟到的回复道歉。我落后于另一个项目的时间表,所以我没有时间对BalusC
和kiran
答案做出正确的回复。
kiran
问我这个: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.
答案:您好kiran,最重要的是,我希望在应用客户端和Web模块端访问同一组逻辑。喜欢glassfish应用服务器。您有一个访问相同资源的命令行版本和Web模块版本。
如果我只是使用应用程序客户端, BalusC
建议使用RESOURCE_LOCAL
工作得很好。但是,一旦我在它上面添加了Web模块,即使BalusC确实尝试与我合作,我也无法让它继续工作。你们可以阅读BalusC的回复以及我对上述想法的实现(原帖中的代码是我对BalusC想法的实现)。如果我将App Client添加到Java EE项目(EAR)类路径中,当我尝试从App Client访问逻辑时它不会收到编译错误,但是当我运行Web模块时,它会生成Exception,表示它没有看到我尝试从App Client访问的类/方法。因此,我决定将所有逻辑移植到EJB上,使我的App Client非常薄。逻辑将通过javax.ejb.Remote
接口向App Client公开,并通过javax.ejb.Local
接口公开给Web模块。以下是我对这个想法的总体布局。
这是我的thin
应用客户端(主要)
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端,包含EJB和Web模块的Java EE项目(EAR)中的哪个包,我有
@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
。