Nutz+ExtJS示例教程——后台Service实现

 

 

后台的结构图如下:

 

 

这里是模仿了SSH的组织方式,因为毕竟大部分是学SSH过来的(ME也是其中之一),变化太大可能会有理解上的困难。

 

这里的Dao层被去掉了,因为Nutz本身提供的NutzDao就提供了基本的增删改查操作,因此这层可以去掉了,直接并入到Service层中。

 

接下来,先写model层,就是对于数据库表的JavaBean

详细步骤请参照Nutz 的文档Dao手册这部分:http://code.google.com/p/nutz/wiki/dao_hello

 

以User举例,其他模块类似

 

User类:

 

package org.nutz.demo.model;

import java.util.List;

import org.nutz.dao.entity.annotation.Column;
import org.nutz.dao.entity.annotation.Many;
import org.nutz.dao.entity.annotation.Table;

/**
 * 用户。
 * 
 * @author [email protected]
 * 
 */
@Table("t_user")
public class User extends Identity {

	@Column()
	private String userName;

	@Column()
	private String password;

	@Column()
	private String userType;

	@Many(target = ContactType.class, field = "userId")
	private List<ContactType> contactTypes;

	@Many(target = Contact.class, field = "userId")
	private List<Contact> contacts;

	@Many(target = Blog.class, field = "userId")
	private List<Blog> blogs;

	//  这里省略了get与set

}

 

要注意的是,不要忘了加Nutz的注释,同时如果字段与表中列名称有出入的话,要写入@Column("表列名")中,替换掉默认值。

 

下面开始写Service层共通的类,所有的Service都要继承这个基类,就可以实现增删改查的操作了。

 

这里对于增删改三项(查询操作的返回值就是查询的结果集,没有封装的必要)操作的返回值,做了一个简单的封装,其中包含了一部分业务信息,用一个枚举类型代替默认的返回值。

 

DbOperationResultEnum :

package org.nutz.demo.util.database;

/**
 * 封装了各种数据库操作结果。
 * 
 * @author [email protected]
 * 
 */
public enum DbOperationResultEnum {

	OPERATION_SUCCESS(true, "操作成功。"),

	OPERATION_FAILURE(false, "操作失败。"),

	INSERT_SUCCESS(true, "插入成功。"),

	INSERT_FAILURE(false, "插入失败。"),

	UPDATE_SUCCESS(true, "更新成功。"),

	UPDATE_FAILURE(false, "更新失败。"),

	DELETE_SUCCESS(true, "删除成功。"),

	DELETE_FAILURE(false, "删除失败,数据可能被引用,请先删引用关系。"),

	CLEAR_SUCCESS(true, "批量删除成功。"),

	CLEAR_FAILURE(false, "批量删除失败,数据可能被引用,请先删引用关系。");

	private boolean success;

	private String msg;

	private DbOperationResultEnum(boolean success, String msg) {
		this.success = success;
		this.msg = msg;
	}

	public boolean isSuccess() {
		return success;
	}

	public String getMsg() {
		return msg;
	}
}
 

BaseService:

package org.nutz.demo.service;

import java.util.List;

import org.nutz.dao.Chain;
import org.nutz.dao.Condition;
import org.nutz.dao.QueryResult;
import org.nutz.dao.sql.Sql;
import org.nutz.demo.util.database.DbOperationResultEnum;

/**
 * CRUD基本操作。<br>
 * 基于泛型类。
 * 
 * @author [email protected]
 * 
 * @param <T>
 *            实体类类型
 */
public interface BaseService<T> {

	/**
	 * 从配置SQL文件中取得SQL文。
	 * 
	 * @param key
	 * @return
	 */
	public Sql createSql(String key);

	/**
	 * 直接执行一组SQL语句。
	 * 
	 * @param sqls
	 * @return
	 */
	public boolean execute(Sql... sqls);

	/**
	 * 通用查询。(无分页)
	 * 
	 * @param cdn
	 * @return
	 */
	public List<T> query(Condition cdn);

	/**
	 * 通用查询。(带分页信息)
	 * 
	 * @param cdn
	 * @param pageNumber
	 * @param pageSize
	 * @return
	 */
	public QueryResult query(Condition cdn, int pageNumber, int pageSize);

	/**
	 * 通用获取,对象中引用的对象。(根据正则匹配)
	 * 
	 * @param obj
	 * @param regex
	 * @return
	 */
	public T fetchLinks(T obj, String regex);

	/**
	 * 通用获取。(根据条件)
	 * 
	 * @param cdn
	 * @return
	 */
	public T fetch(Condition cdn);

	/**
	 * 通用获取。(根据id)
	 * 
	 * @param id
	 * @return
	 */
	public T fetch(long id);

	/**
	 * 通用获取。(根据name)
	 * 
	 * @param name
	 * @return
	 */
	public T fetch(String name);

	/**
	 * 通用删除。(根据实体)
	 * 
	 * @param entity
	 * @return
	 */
	public DbOperationResultEnum delete(T entity);

	/**
	 * 通用插入。(根据实体)
	 * 
	 * @param entity
	 * @return
	 */
	public DbOperationResultEnum insert(T entity);

	/**
	 * 通用更新。(根据条件)
	 * 
	 * @param entity
	 * @return
	 */
	public DbOperationResultEnum update(Condition cnd, Chain chain);

	/**
	 * 通用更新。(根据实体)
	 * 
	 * @param entity
	 * @return
	 */
	public DbOperationResultEnum update(T entity);

	/**
	 * 通用批量删除。(删除全部)
	 * 
	 * @return
	 */
	public DbOperationResultEnum clear();

	/**
	 * 通用批量删除。(根据条件删除)
	 * 
	 * @param cdn
	 * @return
	 */
	public DbOperationResultEnum clear(Condition cdn);

	/**
	 * 返回该Service使用的实体类类型。
	 * 
	 * @return
	 */
	public Class<T> getEntryClz();

}

其中的方法可以根据你的需求再自行添加。

 

 

这里写到后来才发现,BaseService这个基类中的功能与Nutzorg.nutz.service 包下的几个类,EntityService,IdEntityServiceNameEntityService提供的功能相似

 

大家可以根据情况直接使用Nutz提供的,或作为参考,根据自身情况写出更符合自己使用习惯的共通类,来进行复用。

 

 

下面是BaseService的实现类:

package org.nutz.demo.service.impl;

import java.util.List;

import org.nutz.dao.Chain;
import org.nutz.dao.Condition;
import org.nutz.dao.Dao;
import org.nutz.dao.QueryResult;
import org.nutz.dao.impl.NutDao;
import org.nutz.dao.pager.Pager;
import org.nutz.dao.sql.Sql;
import org.nutz.demo.exception.BizException;
import org.nutz.demo.service.BaseService;
import org.nutz.demo.util.DaoUtil;
import org.nutz.demo.util.database.DSConfig;
import org.nutz.demo.util.database.DbOperationResultEnum;
import org.nutz.lang.Mirror;
import org.nutz.log.Log;
import org.nutz.log.Logs;

/**
 * 通用操作 实现。<br>
 * 
 * @author [email protected]
 * 
 */
public abstract class BaseServiceImpl<T> implements BaseService<T> {

	protected Log logger = Logs.getLog(getClass());

	private Mirror<T> mirror;

	@SuppressWarnings("unchecked")
	public BaseServiceImpl() {
		// 尝试获得泛型的类型
		try {
			Class<T> entryClass = (Class<T>) Mirror.getTypeParam(getClass(), 0);
			mirror = Mirror.me(entryClass);
			if (logger.isDebugEnabled())
				logger.debugf("获得泛型的实际类型: %s", entryClass.getName());
		} catch (Throwable e) {
			if (logger.isWarnEnabled())
				logger.warn("!!!无法获得泛型类型!", e);
			throw new RuntimeException(e);
		}
	}

	/**
	 * 方便的提供上层Dao。
	 * 
	 * @return
	 */
	public NutDao getDao() {
		return DaoUtil.getDao();
	}

	/**
	 * 方便的提供上层Dao。
	 * 
	 * @param dbConfig
	 * @return
	 */
	public NutDao getDao(DSConfig dbConfig) {
		return DaoUtil.getDao(dbConfig);
	}

	/**
	 * 获得当前泛型类型。
	 * 
	 * @return
	 */
	public Class<T> getEntryClz() {
		return mirror.getType();
	}

	/**
	 * 从配置SQL文件中取得SQL文。
	 * 
	 * @param key
	 * @return
	 */
	public Sql createSql(String key) {
		return DaoUtil.getSQLDao().sqls().create(key);
	}

	/**
	 * 直接执行一组SQL语句。
	 * 
	 * @param sqls
	 */
	public boolean execute(Sql... sqls) {
		try {
			DaoUtil.getDao().execute(sqls);
			return true;
		} catch (Throwable e) {
			if (logger.isErrorEnabled()) {
				logger.error("批量执行SQL语句报错。", e);
			}
			throw new RuntimeException(e);
		}
	}

	/**
	 * 通用查询。(无分页)
	 * 
	 * @param cdn
	 * @return
	 */
	public List<T> query(Condition cdn) {
		return DaoUtil.getDao().query(getEntryClz(), cdn, null);
	}

	/**
	 * 通用查询。(带分页信息)
	 * 
	 * @param cdn
	 * @param pageNumber
	 * @param pageSize
	 * @return
	 */
	public QueryResult query(Condition cdn, int pageNumber, int pageSize) {
		Dao dao = DaoUtil.getDao();
		Pager pager = dao.createPager(pageNumber, pageSize);
		List<T> list = dao.query(getEntryClz(), cdn, pager);
		if (null != pager) {
			pager.setRecordCount(dao.count(getEntryClz(), cdn));
		}
		return new QueryResult(list, pager);
	}

	/**
	 * 通用获取,对象中引用的对象。(根据正则匹配)
	 * 
	 * @param obj
	 * @param regex
	 * @return
	 */
	public T fetchLinks(T obj, String regex) {
		return DaoUtil.getDao().fetchLinks(obj, regex);
	}

	/**
	 * 通用获取。(根据条件)
	 * 
	 * @param cdn
	 * @return
	 */
	public T fetch(Condition cdn) {
		return DaoUtil.getDao().fetch(getEntryClz(), cdn);
	}

	/**
	 * 通用获取。(根据id)
	 * 
	 * @param id
	 * @return
	 */
	public T fetch(long id) {
		return DaoUtil.getDao().fetch(getEntryClz(), id);
	}

	/**
	 * 通用获取。(根据name)
	 * 
	 * @param name
	 * @return
	 */
	public T fetch(String name) {
		return DaoUtil.getDao().fetch(getEntryClz(), name);
	}

	/**
	 * 通用删除。(根据实体)
	 * 
	 * @param entity
	 * @return
	 */
	public DbOperationResultEnum delete(T entity) {
		try {
			return 1 == DaoUtil.getDao().delete(entity) ? DbOperationResultEnum.DELETE_SUCCESS
					: DbOperationResultEnum.DELETE_FAILURE;
		} catch (Throwable e) {
			if (logger.isErrorEnabled()) {
				logger.error("删除数据出错。", e);
			}
			throw new BizException(DbOperationResultEnum.DELETE_FAILURE, e);
		}
	}

	/**
	 * 通用插入。(根据实体)
	 * 
	 * @param entity
	 * @return
	 */
	public DbOperationResultEnum insert(T entity) {
		try {
			DaoUtil.getDao().insert(entity);
			return DbOperationResultEnum.INSERT_SUCCESS;
		} catch (Throwable e) {
			if (logger.isErrorEnabled()) {
				logger.error("插入数据出错。", e);
			}
			throw new BizException(DbOperationResultEnum.INSERT_FAILURE, e);
		}
	}

	/**
	 * 通用更新。(根据条件)
	 * 
	 * @param entity
	 * @return
	 */
	public DbOperationResultEnum update(Condition cnd, Chain chain) {
		try {
			return DaoUtil.getDao().update(getEntryClz(), chain, cnd) >= 0 ? DbOperationResultEnum.UPDATE_SUCCESS
					: DbOperationResultEnum.UPDATE_FAILURE;
		} catch (Throwable e) {
			if (logger.isErrorEnabled()) {
				logger.error("更新数据出错。", e);
			}
			throw new BizException(DbOperationResultEnum.UPDATE_FAILURE, e);
		}
	}

	/**
	 * 通用更新。(根据实体)
	 * 
	 * @param entity
	 * @return
	 */
	public DbOperationResultEnum update(T entity) {
		try {
			return 1 == DaoUtil.getDao().update(entity) ? DbOperationResultEnum.UPDATE_SUCCESS
					: DbOperationResultEnum.UPDATE_FAILURE;
		} catch (Throwable e) {
			if (logger.isErrorEnabled()) {
				logger.error("更新数据出错。", e);
			}
			throw new BizException(DbOperationResultEnum.UPDATE_FAILURE, e);
		}
	}

	/**
	 * 通用批量删除。(删除全部)
	 * 
	 * @return
	 */
	public DbOperationResultEnum clear() {
		return clear(null);
	}

	/**
	 * 通用批量删除。(根据条件删除)
	 * 
	 * @param cdn
	 * @return
	 */
	public DbOperationResultEnum clear(Condition cdn) {
		try {
			return DaoUtil.getDao().clear(getEntryClz(), cdn) >= 0 ? DbOperationResultEnum.CLEAR_SUCCESS
					: DbOperationResultEnum.CLEAR_FAILURE;
		} catch (Throwable e) {
			if (logger.isErrorEnabled()) {
				logger.error("批量删除数据出错。", e);
			}
			throw new BizException(DbOperationResultEnum.CLEAR_FAILURE, e);
		}
	}
}

其中导入的几个新类要简单介绍下:

 

DaoUtil类:

 

package org.nutz.demo.util;

import org.nutz.dao.impl.FileSqlManager;
import org.nutz.dao.impl.NutDao;
import org.nutz.demo.util.database.DSConfig;
import org.nutz.demo.util.database.DSContainer;
import org.nutz.demo.util.database.DSUtil;
import org.nutz.log.Log;
import org.nutz.log.Logs;

/**
 * DAO层切换类。
 * 
 * @author [email protected]
 * 
 */
public abstract class DaoUtil {

	protected static Log logger = Logs.getLog(DaoUtil.class);

	private static NutDao sqlDao = new NutDao();

	private static NutDao defaultDao = new NutDao(DSContainer.getDataSource(DSUtil
			.getDefaultDSConfig()));

	/**
	 * 加载配置SQL
	 */
	static {
		sqlDao.setSqlManager(new FileSqlManager("/config/sql"));
		if (logger.isDebugEnabled()) {
			logger.debugf("加载SQL配置文件成功,共%s条", sqlDao.sqls().count());
			for (String key : sqlDao.sqls().keys()) {
				String sql = sqlDao.sqls().get(key);
				logger.debugf("Key: %s Value: %s", key, sql);
			}
		}
	}

	public static NutDao getSQLDao() {
		return sqlDao;
	}

	/**
	 * 获得当前选中的Dao。
	 * 
	 * @return
	 */
	public static NutDao getDao() {
		return defaultDao;
	}

	/**
	 * 根据数据源配置信息,获得Dao。
	 * 
	 * @param dbConfig
	 * @return
	 */
	public static NutDao getDao(DSConfig dbConfig) {
		return new NutDao(DSContainer.getDataSource(dbConfig));
	}

}

这个类负责返回普通的NutzDao,跟一个用来获得自定义SQL语句的特殊的NutzDao

 

这里涉及到这个包下的几个类:

 

大家可能很奇怪为什么这里代码要这么多,这里其实从现有项目中搬过来的,是因为在做的项目中有这样一个需求——适应多数据源。

 

这个适应不只是在配置文件的时候要可以使用不同的数据源,在运行过程中,也要可以随时加入新的数据源,一个Service的每次执行都可以指定使用不同的数据源。

 

当然这里的系统不会有这么复杂的情况,代码的话大家可以选择性的看看,后面有时间了ME会把这部分精简掉。

 

BizException类:

package org.nutz.demo.exception;

import org.nutz.demo.util.database.constant.DbOperationResultEnum;

/**
 * 一个包含异常信息跟数据库操作结果信息的异常类。
 * 
 * @author [email protected]
 * 
 */
@SuppressWarnings("serial")
public class BizException extends RuntimeException {

	private DbOperationResultEnum dbOperationResultEnum = null;

	private Throwable cause = null;

	/**
	 * 业务异常。
	 * 
	 * @param dbOperationResultEnum
	 * @param cause
	 */
	public BizException(DbOperationResultEnum dbOperationResultEnum, Throwable cause) {
		super(cause);
		this.cause = cause;
		this.dbOperationResultEnum = dbOperationResultEnum;
	}

	public Throwable getCause() {
		return this.cause;
	}

	public DbOperationResultEnum getDBResult() {
		return this.dbOperationResultEnum;
	}
}

包含异常信息跟数据库操作结果信息的异常类,这里是为了保证在数据库出错的时候,仍然能正常的在页面上返回合理的结果,并将这个异常记录下来,这个类会跟后面的AOP配合使用,对其进行拦截,并将其异常写入日志,其结果返回页面。

 

好了,接下来开始实现四个模块的Service

 

这里以User为例子,展示Service接口与实现如何编写。


接口:

package org.nutz.demo.service;

import org.nutz.demo.model.User;

/**
 * 用户模块Service接口。
 * 
 * @author [email protected]
 * 
 */
public interface UserService extends BaseService<User> {

}
 

实现:

package org.nutz.demo.service.impl;

import org.nutz.demo.model.User;
import org.nutz.demo.service.UserService;

/**
 * 用户模块Service实现类。
 * 
 * @author [email protected]
 * 
 */
public class UserServiceImpl extends BaseServiceImpl<User> implements UserService {

}

是不是很简单,就这样,基本的增删改查功能就有了,那么接口与实现还存在的意义,就是在其中加入某个模块所具有的特殊的业务逻辑,而基本CRUD操作实现,你可以一行代码都不用写了,都在基类中实现好了。

 

最终代码如下:

 

下面把数据库配置文件加入,然后写几个测试类,看看刚才写的Service是否成功。

 

数据库配置文件ME这里采用了过去常用的properties文件,因为很多人还不是太熟悉用json格式(好吧,其实当时是ME忘了用json了)

 

 

放在这里,同时这里可以放一个log4j的配置文件,反正早晚都需要。

 

下面是测试类,还是以User为例,其他类似。

 

package org.nutz.demo.service;

import java.util.List;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.nutz.dao.Cnd;
import org.nutz.demo.model.User;
import org.nutz.demo.service.impl.UserServiceImpl;
import org.nutz.demo.util.database.constant.DbOperationResultEnum;

public class UserServiceTest {

	private static UserService userService;

	// 测试数据
	private final static String USERNAME1 = "testUser1";
	private final static String USERNAME2 = "testUser2";
	private final static String USERNAME3 = "testUser3";
	private final static String USERNAME4 = "testUser4";
	private final static String USERNAME4_2 = "testUser4_2";
	private final static String PASSWORD = "test123";

	private final static String ADMIN = "管理员";
	private final static String USER = "普通用户";

	@BeforeClass
	public static void beforeClass() {
		userService = new UserServiceImpl();
		// 准备测试数据
		User user1 = new User();
		user1.setUserName(USERNAME1);
		user1.setPassword(PASSWORD);
		user1.setUserType(ADMIN);

		User user2 = new User();
		user2.setUserName(USERNAME2);
		user2.setPassword(PASSWORD);
		user2.setUserType(USER);

		User user3 = new User();
		user3.setUserName(USERNAME3);
		user3.setPassword(PASSWORD);
		user3.setUserType(USER);

		// 清理当前数据
		userService.clear();
		// 插入测试数据
		userService.insert(user1);
		userService.insert(user2);
		userService.insert(user3);
	}

	@AfterClass
	public static void afterClass() {
		// 清理所有测试数据
		userService.clear();
	}

	@Before
	public void beforeMethod() {

	}

	@After
	public void afterMethod() {

	}

	@Test
	public void insert() throws Exception {
		User user = new User();
		user.setUserName(USERNAME4);
		user.setPassword(PASSWORD);
		user.setUserType(ADMIN);
		DbOperationResultEnum result = userService.insert(user);
		Assert.assertTrue(result.isSuccess());
	}

	@Test
	public void fetch() throws Exception {
		User user = userService.fetch(Cnd.where("userName", "=", USERNAME1));
		Assert.assertNotNull(user);
		Assert.assertTrue("管理员".equals(user.getUserType()));
	}

	@Test
	public void update() throws Exception {
		User user = userService.fetch(Cnd.where("userName", "=", USERNAME4));
		user.setUserName(USERNAME4_2);
		user.setUserType(USER);
		DbOperationResultEnum result = userService.update(user);
		Assert.assertTrue(result.isSuccess());
	}

	@Test
	public void query() throws Exception {
		List<User> users1 = userService.query(Cnd.where("userName", "=", USERNAME2).and("userType",
				"=", USER));
		List<User> users2 = userService.query(null);
		Assert.assertTrue(users1.size() == 1);
		Assert.assertTrue(users2.size() == 4);
	}

	@Test
	public void delete() throws Exception {
		User user = new User();
		user.setId(1000000);
		DbOperationResultEnum result = userService.delete(user);
		Assert.assertFalse(result.isSuccess());
	}

	@Test
	public void clear() throws Exception {
		DbOperationResultEnum result = userService.clear(Cnd.where("userName", "=", USERNAME4_2));
		Assert.assertTrue(result.isSuccess());
	}
}

 

确认无误后,说明后台的增删改查基本功能已经没有问题,可以开始进行下面的编码了。

 

下一集,将讲述MVC与IoC的使用。


PS:附件中为项目源码,无需解压直接导入Eclipse即可。

你可能感兴趣的:(DAO,sql,JUnit,配置管理,Gmail)