貌似最近EJB3.0很流行\自己也开始学了下,其实如果对spring、hibernate比较熟悉的话ejb3.0其实很简单
ejb3.0也有依赖注入、AOP事务管理及hibernate的延迟加载这些东西和spring、hibernate大致做法是一致的!
学习ejb3.0之前必须先理解如下:
1、什么是会话bean、什么是实体bean(bean不知道中文对应的字,写成豆有点别扭 ,呵呵文盲sessionBean、entityBean)
//插一句什么是消息驱动bean 其实这个实例中没有介绍,所以我也不说明了~
2、有状态bean和无状态bean的区别
开发环境及工具:
1、jdk1.5
2、jboss-4.2.3.GA(必须4.2.xx版本才支持ejb3.0啊)
3、MyEclipse 5.5.1 GA
4、mysql
我使用MyEclipse 5.5.1 GA 主要是工作中习惯了它,其它高版本还不太适应,高版本建立ejb3.0应该一样89不离10哈
如果只有EclipseJ2EE 版本的话就需要将jboos中的D:\jboss-4.2.3.GA\server\default\lib的jar文件加入到根目录,这个不做介绍了哈,本机没有jee版本
EJB都是通过容器来连接数据库的,程序通过JNDI来调用
在建立项目之前先进入jboss 目录:D:\jboss-4.2.3.GA\docs\examples\jca 找到mysql-ds.xml ,因为我使用的是mysql所以就要它另存为其它地方,如果是oracle或db2找相对应的就OK
然后修改下数据库连接及其它设置修改如下
<?xml version="1.0"
encoding="UTF-8"?>
<datasources>
<local-tx-datasource>
<jndi-name>demoDS</jndi-name>
<connection-
url>jdbc:mysql://127.0.0.1:3306/demo</connection-url>
<driver-class>com.mysql.jdbc.Driver</driver-class>
<user-name>root</user-name>
<password>root</password>
<min-pool-size>3</min-pool-size>
<max-pool-size>10</max-pool-size>
<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>
以上数据库设置就好了,不需要写代码哦···简单的爽·················然后把修改后的配置文件复制到jboss目录:D:\jboss-4.2.3.GA\server\default\deploy下
启动jboss数据库连接就O了·
请记住<jndi-name>demoDS</jndi-name> 后面项目建立的时候需要用到
现在开始建立EJB3.0项目
内容如下:
<?xml version="1.0"
encoding="UTF-8"?>
<persistence
xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="demo" transaction-
type="JTA">
<jta-data-source>java:demoDS</jta-data-
source>
</persistence-unit>
</persistence>
transaction-type="JTA" 一定要是JTA事物啊,这个可是支持分布式事物的啊!
项目各种包的一个展示,对应有过开发项目的人来说应该是明白的我还是具体说明下
org.demo.jpa.dao dao层的接口
org.demo.jpa.dao.impl dao层的实现类
org.demo.model 这里指实体bean说简单点就是对应数据库的java bean对象(这样说有一点不对,它不是简单java bean,它属于ejb的bean)
org.demo.service 服务层的接口
org.demo.service.impl 服务层实现类
META-INF这个是JTA规范
首先数据库建立两张表,user表和book表,本打算介绍下懒加载的,但因为时间问题,这个以后在说了,暂时不介绍了,所以实际只用到了user表
我是反向工程,先建立表结构然后通过工具生成数据库javaBean模型
数据库代码:
-- Table "user" DDL
CREATE TABLE `user` (
`id` int(11) NOT NULL auto_increment,
`userName` varchar(10) NOT NULL,
`passWord` varchar(10) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `userName` (`userName`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- Table "book" DDL
CREATE TABLE `book` (
`id` int(11) NOT NULL auto_increment,
`bookName` varchar(255) NOT NULL,
`user` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `user` (`user`),
CONSTRAINT `book_ibfk_1` FOREIGN KEY (`user`) R
数据库表建立好后,现在通过MyEclipse来自动生成代码
右上角--字打错了,改 :如果没有上面红线部分点击other可以找到
反转好后model包下就有如下两个java代码,由于用了工具生成,但工具未帮我们设置主键增长方式
需要我们手动添加代码,点击User.java、Book.java 找到 public Integer getId()方法添加如下代码
@GeneratedValue(strategy=GenerationType.AUTO) 这个是设置默认状态
代码如下
User.java
package org.demo.model;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
/**
* User generated by MyEclipse Persistence Tools
*/
@Entity
@Table(name = "user", catalog = "demo", uniqueConstraints = {})
public class User implements java.io.Serializable {
/**
*
*/
private static final long serialVersionUID = 5080524625071021061L;
private Integer id;
private String userName;
private String passWord;
private Set<Book> books = new HashSet<Book>(0);
// Constructors
/** default constructor */
public User() {
}
/** minimal constructor */
public User(Integer id, String userName, String passWord) {
this.id = id;
this.userName = userName;
this.passWord = passWord;
}
/** full constructor */
public User(Integer id, String userName, String passWord, Set<Book> books) {
this.id = id;
this.userName = userName;
this.passWord = passWord;
this.books = books;
}
// Property accessors
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
@Column(name = "id", unique = true, nullable = false, insertable = true, updatable = true)
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
@Column(name = "userName", unique = true, nullable = false, insertable = true, updatable = true, length = 10)
public String getUserName() {
return this.userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
@Column(name = "passWord", unique = false, nullable = false, insertable = true, updatable = true, length = 10)
public String getPassWord() {
return this.passWord;
}
public void setPassWord(String passWord) {
this.passWord = passWord;
}
@OneToMany(cascade = { CascadeType.ALL }, fetch = FetchType.LAZY, mappedBy = "user")
public Set<Book> getBooks() {
return this.books;
}
public void setBooks(Set<Book> books) {
this.books = books;
}
}
book.java
package org.demo.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
/**
* Book generated by MyEclipse Persistence Tools
*/
@Entity
@Table(name = "book", catalog = "demo", uniqueConstraints = {})
public class Book implements java.io.Serializable {
// Fields
/**
*
*/
private static final long serialVersionUID = 5006758001351352346L;
private Integer id;
private User user;
private String bookName;
// Constructors
/** default constructor */
public Book() {
}
/** full constructor */
public Book(Integer id, User user, String bookName) {
this.id = id;
this.user = user;
this.bookName = bookName;
}
// Property accessors
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
@Column(name = "id", unique = true, nullable = false, insertable = true, updatable = true)
public Integer getId() {
return this.id;
}
public void setId(Integer id) {
this.id = id;
}
@ManyToOne(cascade = {}, fetch = FetchType.LAZY)
@JoinColumn(name = "user", unique = false, nullable = false, insertable = true, updatable = true)
public User getUser() {
return this.user;
}
public void setUser(User user) {
this.user = user;
}
@Column(name = "bookName", unique = false, nullable = false, insertable = true, updatable = true)
public String getBookName() {
return this.bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
}
现在模型生成好了,现在写dao层代码
首先我是把dao层抽象出来,不是每一service对应一个dao,而是所有的service调用一个dao
这个也是我们公司一直这样做的,JpaDao.java这个接口,我只实现了查询和保持接口,写的比较简单,够这个demo就OK了
JpaDao.java 接口
package org.demo.jpa.dao;
import java.util.List;
import java.util.Map;
public interface JpaDao {
/**
* 查询接口
* @param jpaql jpaql语句
* @param parameters 数组参数
* @return 返回对象数组
*/
public List<Object> find(String jpaql, Object[] parameters);
/**
* 查询接口
* @param jpaql jpaql语句
* @param parameters Map作为参数key指占位符字符串,value指具体的值对象
* @return 返回对象数组
*/
public List<Object> find(String jpaql, Map<String, Object> parameters);
/**
* 保存持久化对象
* @param obj 持久化对象
*/
public void save(Object obj);
}
JpaDaoImpl.java 实现类
package org.demo.jpa.dao.impl;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.ejb.Local;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import org.demo.jpa.dao.JpaDao;
/**
* 这就不做过多的介绍了哈,不明白请查看UserServiceImpl中的注释说明
*
* jpa是什么我大概说下我的理解哈,他就是J2EE官方规范,对orm框架做的一个统一接口
* 就有点类似于jdbc规范,sun只给一个jdbc接口,具体如何实现由oracle\mysql\db2\....来实现
* 还是那句话可能说的不完全正确,但说到点子上就OK
* 如果学过hibernate 对jpa学起来就快了,HQL和JPAQL 也非常类似
* @author Administrator
*
*/
@Stateless
@Local //申明一个本地的会话并\兵\饼(sessionBean不知道中文应该怎么打bean)
public class JpaDaoImpl implements JpaDao {
//申明一个持久化上下文\这个就是jta(JTA 事务定义管理)规范 unitName="demo"
// demo 这个字符串是 /META-INF/persistence.xml文件中的
// <persistence-unit name="demo" transaction-type="JTA"> 的name字段
// EntityManager em; 实体管理器,这里就有点像hibernate中的sessionFactory了哈,个人理解的哈
// 不一定正确
@PersistenceContext(unitName="demo") EntityManager em;
//@SuppressWarnings("unchecked") 这个不是ejb的注释,这是一个去除警告的注释
@SuppressWarnings("unchecked")
public List<Object> find(String jpaql, Object[] parameters) {
//查询接口
Query query = em.createQuery(jpaql);
for (int i = 0; i < parameters.length; i++) {
Object parameter = parameters[i];
query.setParameter(i+1, parameter);
}
return query.getResultList();
}
@SuppressWarnings("unchecked")
public List<Object> find(String jpaql, Map<String, Object> parameters) {
Query query = em.createQuery(jpaql);
Iterator<Entry<String, Object>> it = parameters.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, Object> entry = it.next();
query.setParameter(entry.getKey(), entry.getValue());
}
return query.getResultList();
}
public void save(Object obj) {
//类似hibernate.save(Object ojb);
em.persist(obj);
}
}
做到现在Dao层就完成了
现在开始实现service层
UserService.java 接口
package org.demo.service;
import java.util.List;
import org.demo.model.User;
public interface UserService {
public User verifyUser(String username,String password);
public void saveUser(User user);
public void saveUser(List<User> users);
}
UserServiceImpl.java 实现类
注释写的很清楚了哈,·
package org.demo.service.impl;
import java.util.List;
import javax.ejb.EJB;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import org.demo.jpa.dao.JpaDao;
import org.demo.model.User;
import org.demo.service.UserService;
/*这是一个可远程访问的Session Bean */
@Stateless // @Stateless 声明这是一个无状态sessionBean
@Remote // @Remote 声明这是一个可远程调用的sessionBean,如果未声明默认为local(本地),本地可以节省网络开销,需在同一个jvm中,内存调用
//声明事物 事物分为两种(CMP、BMP)cmp可以理解为api事物(也可理解为用程序来控制事物),bmp可以理解为容器来处理事物(无需要写代码)
//@TransactionManagement(TransactionManagementType.CONTAINER) 我们用的是bmp事物,我完全是用白话文说的哈,可能说的不对,见谅
//我书读的不多,也许说得不够准确,但大概意思是说到了的,如果不明白建议google、baidu
@TransactionManagement(TransactionManagementType.CONTAINER)
public class UserServiceImpl implements UserService {
//@EJB 这里指调用同一个jvm的ejb对象
@EJB JpaDao jpaDao;
/*验证用户信息*/
public User verifyUser(String username, String password) {
List<Object> users =jpaDao.find("from User u where u.userName=?1 and u.passWord=?2", new Object[]{username,password});
return (User) (users.isEmpty()?null:users.get(0));
}
//保持用户信息
//常用的事物类型2种
// 1、REQUIRED 大概意思是,如果事物不存在创建一个,如果事物存在就调用当前事物,还是那句话不明白就google
// 2、REQUIRES_NEW 大概意思,无论事物是否存在都,再次创建一个事物
@TransactionAttribute(TransactionAttributeType.REQUIRED)//这里声明一个事物类型
public void saveUser(User user) {
jpaDao.save(user);
}
// 做一个类似批量保存,本应该抽象在dao层来做批量,但为了事物所以写在这个方法中~ 加入事物后保持一致性,如果有一条不成功,回滚
// 这个就有点像spring的aop面向切面编程,意思就是这个方法开始之前执行一个行为,结束之前在执行一个行为,事物也就可以用在这里了
// 调用public void saveUser(List<User> users)这个方法之前打开一个事物,当这个方法执行完之前提交事物,并关闭当前会话session
// 自我感觉说的很明白了哈,如果还是无法理解请google..呵呵
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void saveUser(List<User> users) {
for(User user : users){
jpaDao.save(user);
}
}
}
好了现在model、dao、service 都做好了,那么就代表EJB服务端已经完成
现在开始做客户端远程调用的例子了,我把客户端,写到另外一个项目里,这个项目是一个普通的java项目
当然web项目也可以实现,原理是一样的~,web项目写着麻烦,在jsp里写,我是不太习惯,在servlet里写比较好,但麻烦所以,就用一个main函数搞定
建立一个普通的java项目,这个任何一个人都会,我就不啰嗦了~
项目名称叫:Test
项目结构如下
项目搭建好后,引入EJB项目,如下图
点OK后EJB项目引用就完成了~
现在还没完需要进入jboss目录:D:\jboss-4.2.3.GA\client 找到一个名为:jbossall-client.jar 复制一下,第二排第六个,呵呵我的是,不知道你的是不是???
粘贴到刚才建立的Test 项目根目录
我的是没有src的,有src的请放到src下面,然后如图
弄好后,就会出现一个瓶子状的东西,就代表jar文件引用成功
再然后 进入jboss目录:D:\jboss-4.2.3.GA\server\default\conf 找到一个叫jndi.properties的文件复制到根目录
修改成如下: 这东西不想过多的说明,jboos统一的上下文哈,不需要去背,
java.naming.provider.url=localhost:1099 这个就是调用ejb的地址和端口号,这个端口号可以去jboss目录:D:\jboss-4.2.3.GA\server\default\conf
jboss-service.xml中去修改或查找
# DO NOT EDIT THIS FILE UNLESS YOU KNOW WHAT YOU ARE DOING
#
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
java.naming.provider.url=localhost:1099
现在编写Test.java类
package com.test;
import java.util.ArrayList;
import java.util.List;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.demo.model.User;
import org.demo.service.UserService;
public class Test {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
//初始化上下文 jndi.properties
InitialContext ic = new InitialContext();
//ic.lookup("UserServiceImpl/remote");这个就是调用EJB会话饼(sessionBean)
//remote就是调用远程的哈,我用的是一个main函数所以不在同一个jvm里面,必须使用远程调用
//local是本地调用,如果你用xx.jsp而且在ejb同一个项目中那么就可以本地调用了
UserService userSercice = (UserService) ic.lookup("UserServiceImpl/remote");
//用户查询(模拟登录) 现在数据库没有数据所以 返回 "无此用户"
User user = userSercice.verifyUser("xiaomaha", "qq123"); //查询操作
System.out.println(null==user?"无此用户":user.getUserName());
System.out.println("1:-----------------------分割线-----------------------");
//添加一个用户(模拟注册,验证免了哈)
User user2 = new User();
user2.setUserName("xiaomaha");
user2.setPassWord("qq123");
userSercice.saveUser(user2); //保存操作
System.out.println("保存完成!!");
System.out.println("2:-----------------------分割线-----------------------");
//批量添加用户(这个主要用来测试事物\如果有一条失败就回滚)
//userName数据库设置成唯一约束,所以执行后xiaomaha2\xiaomaha都不应该存在于数据库中(回滚)
List<User> users = new ArrayList<User>();
//第一个user对象
User user3 = new User();
user3.setUserName("xiaomaha2");
user3.setPassWord("qq123");
//第二个user对象
User user4 = new User();
user4.setUserName("xiaomaha"); //前面已经保存了xiaomaha,这里在保存就会有异常
user4.setPassWord("qq123111");
//保存到list中
users.add(user3);
users.add(user4);
//调用ejb sessionBean接口
userSercice.saveUser(users);
System.out.println("3:-----------------------分割线-----------------------");
//用户查询(模拟登录) 现在数据库已经有了 返回 当前用户的名称
User user5 = userSercice.verifyUser("xiaomaha", "qq123"); //查询操作
System.out.println(null==user5?"无此用户":user5.getUserName());
//最后的数据库里面如果只有xiaomaha这个用户那么就代表程序完成
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
最后执行会有异常因为事务回滚了,我是故意这样做的~为了演示事物·····
数据库数据··
不想看到事物回滚,请让userName的值不要重复就不会有异常了,这个大家都知道,貌似我说了也是白说,贴下无异常代码
package com.test;
import java.util.ArrayList;
import java.util.List;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.demo.model.User;
import org.demo.service.UserService;
public class Test {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
//初始化上下文 jndi.properties
InitialContext ic = new InitialContext();
//ic.lookup("UserServiceImpl/remote");这个就是调用EJB会话饼(sessionBean)
//remote就是调用远程的哈,我用的是一个main函数所以不在同一个jvm里面,必须使用远程调用
//local是本地调用,如果你用xx.jsp而且在ejb同一个项目中那么就可以本地调用了
UserService userSercice = (UserService) ic.lookup("UserServiceImpl/remote");
//用户查询(模拟登录) 现在数据库没有数据所以 返回 "无此用户"
User user = userSercice.verifyUser("xiaomaha", "qq123"); //查询操作
System.out.println(null==user?"无此用户":user.getUserName());
System.out.println("1:-----------------------分割线-----------------------");
//添加一个用户(模拟注册,验证免了哈)
User user2 = new User();
user2.setUserName("xiaomaha10");
user2.setPassWord("qq123");
userSercice.saveUser(user2); //保存操作
System.out.println("保存完成!!");
System.out.println("2:-----------------------分割线-----------------------");
//批量添加用户(这个主要用来测试事物\如果有一条失败就回滚)
//userName数据库设置成唯一约束,所以执行后xiaomaha2\xiaomaha都不应该存在于数据库中(回滚)
List<User> users = new ArrayList<User>();
//第一个user对象
User user3 = new User();
user3.setUserName("xiaomaha11");
user3.setPassWord("qq123");
//第二个user对象
User user4 = new User();
user4.setUserName("xiaomaha12"); //前面已经保存了xiaomaha,这里在保存就会有异常
user4.setPassWord("qq123111");
//保存到list中
users.add(user3);
users.add(user4);
//调用ejb sessionBean接口
userSercice.saveUser(users);
System.out.println("3:-----------------------分割线-----------------------");
//用户查询(模拟登录) 现在数据库已经有了 返回 当前用户的名称
User user5 = userSercice.verifyUser("xiaomaha12", "qq123111"); //查询操作
System.out.println(null==user5?"无此用户":user5.getUserName());
//最后的数据库里面如果只有xiaomaha这个用户那么就代表程序完成
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
打印结果:
over````````````````bye`