« 利用JavaFX桌面应用程序在浏览器中打... | Main | JavaFX--上传和下载大型文件 »
http://developers.sun.com.cn/blog/functionalca/date/20090625 星期四 2009年06月25日

Netbeans+EJB 3 快速入门

作者:曹祺
Blog: http://blogs.sun.com/greysh
Web: http://www.greysh.com
Email: Qi.Cao@Sun.com
本文链接:
http://developers.sun.com.cn/blog/functionalca/entry/netbeans_ejb_3_%E5%BF%AB%E9%80%9F%E5%85%A5%E9%97%A8
源代码下载:
http://developers.sun.com.cn/blog/functionalca/resource/Greysh/FCA_Greysh_Ejb_Curd.zip   
难度:入门

对于分布式开发,一般使用的就是Web Service或者EJB,本文主要基于Netbeans开发的一个Ejb的增删查改的教程

对于EJB 3.0规范(最新的是EJB 3.1规范,详情见JCP的JSR),期中定义了Java事务处理(JTA),Java持久化API(JPA),Java信息服务(JMS)等规范,JPA来自EJB,但是并不仅仅限于EJB,因为JavaSE中同样可以使用JPA。目前主流支持EJB的服务器有Sun的Glassfish,Bea(已经被Oracle收购)的Weblogic,IBM的WebSphere,以及JBoss(已经被Red hat Linux收购)的JBossAS。

这些厂家在EJB的规范中有各自的实现,比如JPA的规范,Oracle和Sun采用的Toplink作为ORM引擎,以及Apache的OpenJPA,但是最出名的要属于Hibernate(已经被Jboss收购)了,本文采用Netbeans+JBossAS,因此底层的ORM也是用的Hibernate引擎

文件列表如下
Book.java(实体)
BookDao.java(Remote接口,当然你也可以使用Local接口)
BookDaoBean.java(Remote接口的实现)
BookDaoBeanTest.java(采用JUnit 4做单元测试)
persistence.xml(持久化单元配置文件)

另外需要奖mysql的jdbc驱动放在server的lib文件夹
然后配置数据源用JNDI(Java Naming and Directory Interface),在jboss中,数据源必须以-ds.xml结束,例如本例子采用的mysql,需要配置的文件为mysql-ds.xml,将这个文件放在server/deploy即可。代码如下

<?xml version="1.0" encoding="UTF-8"?>
<datasources>
  <local-tx-datasource>
    <jndi-name>mysqldb</jndi-name>
    <connection-url>jdbc:mysql://localhost:3306/db</connection-url>
    <driver-class>com.mysql.jdbc.Driver</driver-class>
    <user-name>root</user-name>
    <password>root</password>
    <exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter</exception-sorter-class-name>
    <metadata>
       <type-mapping>mySQL</type-mapping>
    </metadata>
  </local-tx-datasource>
</datasources>

如果在http://localhost:8080的jmx-console能看到mysql数据源则配置成功

为了演示的方便,book这个实体中仅仅包含三个字段,主键,书的名字和书的出版日期,因为要持久化,所以要继Serializable接口,对于ejb主要采用annotations的配置方式,比如主键要设定它的生产策略只需要加上
 @Id
 @GeneratedValue(strategy=GenerationType.AUTO)
对于配置好的实体,是不需要人工去创建数据表的SQL的语句,他会自动生成数据库的DDL,我这里用的是mysql所以采用AUTO的生成策略,如果是oracle则可以用sequence生成策略,如果用AUTO会自动绑定常用数据库,当然也可以自己写主键算法
然后对应于每个数据库的字段,在get方法上映射数据表,实现ORM映射,例如
  @Column(nullable=false,length=32)
    public String getBookName()

  @Temporal(value=TemporalType.DATE)
    public Date getPublishDate()
这里之所以要 TemporalType.DATE是制定数据库中插入的是日期而不是时间戳等时间类型,定义annotations后,会自动把java的数据类型转化为数据库类型,当然也可以自动创建表和删除表,之所以这样,是因为我们在persistence.xml配置了
<property name="hibernate.hbm2ddl.auto" value="create-drop"/>。
如果不想在重启服务器后删除表,可以把value设置为create,因为jboss的重启也会重新加载persistent.xml

这样,通过刚才的配置,实体的ORM映射便配置好了,对于多表联查,需要制定不同表之间的映射关系,比如OneToMany,ManyToMany等,因为本教程是快速入门,所以便不赘述了.

我在BookDao中定义了常用的增删查改方法并在BookDaoBean去实现,由于默认是由JBOSS的容器去管理各个bean的状态,所以默认的所有bean都是支持事务的。ejb的事物有很多种状体,如果把事务改为not_support也可以人为去干涉,对于每个ejb bean由创建前状态(不关联事务),事务状态,删除状态,以及钝化状态。钝化状态是指位于EJB容器中的某些bean,由于长期不使用,或者容器内存快满的时候,强制把一些bean从内存持久化到硬盘上,当需要的时候重启移动到内存中来。

常用的方法如下
persiste:相当于hibernate的save,底层的sql就是insert语句
find:底层是sql的select语句
update:跟新,底层是sql的update
merge:当容器中无此对象则insert,反之是update
remove:底层是sql的delete语句

如果上述方法有时用起来灵活性不够,可以使用JPA的JPQL,类似hibernate的HQL,这是一种面向对象的查询语句,对于面向对象语句,虽然底层也是sql语句,但是维护性要好一些,比如某个人的某本书的某个书号,在JPQL中便是
Person.Book.BookIsbn = ?
但是对于SQL便是
Person.BookPid= Book.BookPid and Book.BookIsbn=?
BookPid代表Book的主键

另一方面,ejb也支持原生的sql,或者叫本地sql。既是数据库中能直接运行的SQL语句。有时对性能要求很高或者够着JPQL很复杂的时候,可以考虑用本地SQL

使用JPA的这些方法的时候可以不用写SQL语句,比如要插入一本书,只需要先声明一个EntityManager em,然后用em.persist(book);
对于代码中的 
@PersistenceContext(unitName = "db")
是依赖注入,比如常用的Spring或者Google Guice的DI
unitName是持久化单元,在persistent.xml中指定,如果用到多个数据表,而又采用不同的策略,可以指定多个持久化单元

在使用JPA的时候,初学者对find方法和getReference可能有迷惑,这个和hibernate的find/load方法很类似
虽然都是先从Cache中查找,但是查不到后,find会从数据库找,找不到返回null,但是用getReference后,其实不是容器的dao本身,而是他的一个proxy,采用的代理模式,也就是说通过cglib或者jdk的reflect,根据接口去创建一个代理类,这个代理类是ejb生成的,如果找不到后,这个代理类会抛出EntityNotFoundException

当然,使用起来只需要指定持久化的实体,和可序列化的唯一标识符,常用主键,既可以进行查找,比如例程中的
em.find(Book.class, bookPid);

然后读者在运行工程的时候,需要先部署,然后运行JUNIT单元测试
Junit会先初始化
 @BeforeClass
    public static void setUpClass() throws Exception {
        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:org.jnp.interfaces");
        InitialContext ctx = new InitialContext(props);
        dao = (BookDao) ctx.lookup("BookDaoBean/remote");
    }

 然后运行所有的Test进行单元测试。例如
   @Test
    public void testInsertBook() {
        Book book = new Book();
        book.setBookName("Core Java");
        book.setPublishDate(new Date());
        dao.insertBook(book);
    }

然后运行的时候看数据库的变化。

对于EJB的初学者,有以下内容很重要,需要去理解,因为入门教程,因此不做赘述了,这些包括

JPA持久化操作,比如JPQL,然后数据库的操作方法

如何去映射多个类(那些annotations),如果要防止数据库脏读,不可重读,幻想读,如何去配置那些共享锁,跟新锁,快照锁(此类问题需要先看一本数据库相关的书)

EJB的bean是有生命周期的,有些时候如果要保证运行的效率,有些类让其不支持事务,但是这个类中的有些方法需要支持事务,因此事务的配置也非常重要

EJB还有一个规范,叫做JMS,是异步通信的知识,也很重要

最后,对于一个大型的系统,可能会整合其他的发布,比如ejb如何整合web service,SOA的三种方案中,ejb便是一种

对于有兴趣的读者,也可以了解下ejb底层如何实现。Ejb是JavaEE的一项历史比较悠久的技术,也可以去查查Corba,RMI相关的知识点

相关的资料可以查看JCP的JSR,或者西勒,贝茨写的Head First EJB,以及黎活明先生的EJB 3入门经典,当然最好的资料还是ejb的manual



发表于 Sun Functional 校园大使 [JavaEE] ( 六月 25, 2009 04:24 下午 ) Permalink | 评论[0]
评论:

发表一条评论:
  • HTML语法: 禁用