上篇博文其实我们已经对代码做了一定的封装,但是这远远还不够,你有没有发现上篇博文调用的时候还要用myProductDA.pIdx.put(product);这种写法肯定很不舒服,那么本篇我们将再度进行封装,并对增删改查做一定封装。
4.1 添加项目依赖
选择下面任意一种,为你的项目添加相关依赖
4.1.1 Maven项目
如果是Maven项目,那么请添加下列依赖
<dependency>
<groupId>com.sleepycat</groupId>
<artifactId>je</artifactId>
<version>5.0.73</version>
</dependency>
4.1.2 普通项目Oracle 官网快速下载
打开后会看到这个页面,点击下图中所示进行下载即可
然后解压后复制lib文件夹下的jar 到自己的项目中
4.1.3 CSDN 赞助下载
4.2 数据库环境管理工具类
由于BDB Environment 和EntityStore 打开和关闭与业务无关,通常在程序运行中不需要多个实例,因此我们用懒汉式单例设计模式来封装它,这样能确保不会打开环境多次,否则会出现这种异常。
The environment cannot be locked for single writer access.
ENV_LOCKED: The je.lck file could not be locked. Environment is
invalid and must be closed.
BDBEnvironmentManager.java
import java.io.File;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import com.sleepycat.persist.EntityStore;
import com.sleepycat.persist.StoreConfig;
/**
* Berkeley Database Java Edition 环境管理器
*
* @author fairy
*/
public class BDBEnvironmentManager {
private static BDBEnvironmentManager bdbEnvironmentManager = null;
// 数据库环境对象
private static Environment myEnvironment;
// 数据存储基本单元
private static EntityStore myEntityStore;
// 空的构造器
private BDBEnvironmentManager() {
}
public static Environment getMyEnvironment() {
return myEnvironment;
}
public static void setMyEnvironment(Environment myEnvironment) {
BDBEnvironmentManager.myEnvironment = myEnvironment;
}
public static EntityStore getMyEntityStore() {
return myEntityStore;
}
public static void setMyEntityStore(EntityStore myEntityStore) {
BDBEnvironmentManager.myEntityStore = myEntityStore;
}
// 懒汉式
public static BDBEnvironmentManager getInstance(File envHome,Boolean readOnly) {
if (bdbEnvironmentManager == null) {
//添加同步锁,会更快更安全高效
synchronized (BDBEnvironmentManager.class) {
if (bdbEnvironmentManager == null) {
//代码在这里执行确保应用程序中只有一个实例
bdbEnvironmentManager = new BDBEnvironmentManager();
// 创建一个BDB 环境配置对象
EnvironmentConfig myEnvConfig = new EnvironmentConfig();
// 创建一个数据存储配置对象
StoreConfig myStoreConfig = new StoreConfig();
// 设置该环境是否为只读,true 为只读,false 为可读写
myEnvConfig.setReadOnly(readOnly);
// 设置数据存储配置是否为只读,true 为只读,false 为可读写
myStoreConfig.setReadOnly(readOnly);
// 如果该环境不存在是否重建,true 允许重建,false 不可重建
myEnvConfig.setAllowCreate(!readOnly);
// 如果该存储配置不存在是否重建,true 允许重建,false 不可重建
myStoreConfig.setAllowCreate(!readOnly);
// 如果文件不存在则创建
if (!envHome.exists()) {
envHome.mkdir();
}
// 打开 environment 和 entity store
if(myEnvironment==null||myEntityStore==null) {
myEnvironment = new Environment(envHome, myEnvConfig);
myEntityStore = new EntityStore(myEnvironment, "EntityStore", myStoreConfig);
}
}
}
}
return bdbEnvironmentManager;
}
// Close the store and environment.
public static void close() {
// 判断存储对象是否为空
if (myEntityStore != null) {
try {
// 尝试关闭存储对象
myEntityStore.close();
} catch (DatabaseException dbe) {
System.err.println("Error closing store: " + dbe.toString());
}
}
// 判断环境是否为空
if (myEnvironment != null) {
try {
// 关闭环境
myEnvironment.close();
} catch (DatabaseException dbe) {
System.err.println("Error closing MyDbEnv: " + dbe.toString());
}
}
}
}
懒汉模式在使用时,容易引起不同步问题,所以应该创建同步”锁”,这样会同步更快,更安全高效,所以代码中对传统的懒汉式代码做了一定的优化,
关于懒汉式还是用恶汉式的选择,如果单件模式实例在系统中经常会被用到,则建议修改此工具类使用恶汉式,如果很少会被用到,那么使用懒汉式比较好,我们的BDB环境一般初始化一次即可,不需要重复获取实例,因此这里使用了懒汉式。
参考博文:Java单例模式——–懒汉式和饿汉式
好了,上述代码已经对JE Environment和EntityStore做了一定的封装。
4.3 编写业务实体类
现在让我们开始写一个业务实体类,假设我们有一个实体类User,拥有成员变量
userId用户Id,userName用户名称,password 用户密码,email用户邮箱
User.java
import com.sleepycat.persist.model.Entity;
import com.sleepycat.persist.model.PrimaryKey;
import com.sleepycat.persist.model.Relationship;
import com.sleepycat.persist.model.SecondaryKey;
/***
*
* */
//通过这个注解标识这是一个BDB实体类
@Entity
public class User {
//通过添加这个注解标识为主键并让其自增长
@PrimaryKey(sequence = "userId")
public Long userId;//用户ID
//每个用户的账号都不一样,每个用户和账号的关系是一对一
@SecondaryKey(relate=Relationship.ONE_TO_ONE)
public String userName;//用户账号
public String password;//用户密码
// BDB中只有一个主键但是可以有多个二级索引
//一对一 :一个用户确定只有一个邮箱 使用ONETOONE 如果插入发现新用户的邮箱已存在则会抛出异常
//多对一: 多个用户可以使用同一个邮箱
//一对多:如果一个用户可以拥有多个邮箱使用ONETOMANY 且该二级索引字段必须为数组或者集合类型
//多对多:如果多个用户可以拥有多个邮箱使用MANYTOMANY 且该二级索引字段必须为数组或者集合类型
@SecondaryKey(relate=Relationship.MANY_TO_ONE)
public String email;//用户邮箱
}
4.4 封装增删改查DA类
UserDA.java
import java.util.ArrayList;
import java.util.List;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.persist.EntityCursor;
import com.sleepycat.persist.EntityStore;
import com.sleepycat.persist.PrimaryIndex;
import com.sleepycat.persist.SecondaryIndex;
import com.xingyun.model.User;
import com.xingyun.util.BDBEnvironmentManager;
public class UserDA {
/**
* PrimaryIndex<Long,User>
* Long是实体类User中主键的数据类型,User是我们的业务实体类
* */
//主键UserId是Long类型,实体类是User
private static PrimaryIndex<Long, User> userId_Idx;// 主键
/**
* BDB中只有一个主键但是可以有多个二级索引
* SecondaryIndex<String,Long,User>
* String 是二级索引的 数据类型,Long是关联主键的数据类型,User业务实体类
* **/
//UserName 是字符串类型,主键UserId是Long类型,实体类是User
private static SecondaryIndex<String,Long,User> userName_Idx;
// Email 是字符串类型,主键UserId是Long类型,实体类是User
private static SecondaryIndex<String,Long, User> email_Idx;// 二级索引
// 默认的构造函数
public UserDA() {
}
public UserDA(EntityStore entityStore) {
// 获取User对象主键索引
// Long.class 是主键的数据类型,User.class是我们的业务实体类
userId_Idx = entityStore.getPrimaryIndex(Long.class, User.class);
//获取userName索引对象
userName_Idx = entityStore.getSecondaryIndex(userId_Idx, String.class, "userName");
//获取Email索引对象
// 三个参数分别是主键索引对象,二级索引的数据类型,二级索引的字段名称
email_Idx = entityStore.getSecondaryIndex(userId_Idx, String.class, "email");
}
/**
* 添加一个User对象
* @param user要添加的用户对象
* @return true 标识添加成功 false 表示添加失败
**/
public Boolean addUser(User user) {
User checkUser=null;
try {
checkUser=userName_Idx.get(user.userName);
if(checkUser==null) {
//使用主键保存对象
userId_Idx.put(user);
//修改强制同步写入到磁盘
BDBEnvironmentManager.getMyEnvironment().sync();
return true;
}else {
return false;
}
} catch (DatabaseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
}
/**
* 删除一个用户对象
*
* @param userId 用户的主键Id
* @return true 删除成功 false 删除失败
**/
public Boolean removeUserById(Long userId) {
try {
userId_Idx.delete(userId);
//将修改强制同步写入到磁盘
BDBEnvironmentManager.getMyEnvironment().sync();
return true;
} catch (DatabaseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
}
/**
* 根据用户名称删除用户
* @param username 要删除的用户名称
* @return true 删除成功,false 删除失败
* **/
public Boolean removeUserByUserName(String username) {
try {
userName_Idx.delete(username);
return true;
} catch (DatabaseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
}
/**
* 修改保存一个用户
* @param user 要修改的用户对象
* @return true 修改成功 false 修改失败
**/
public Boolean saveUser(User user) {
try {
userId_Idx.put(user);
return true;
} catch (DatabaseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
}
/**
* 根据用户主键Id查询一个用户
* @param userId 用户Id
* @return User 查询到的User对象
**/
public User findUserById(Long userId) {
User user = userId_Idx.get(userId);
return user;
}
/**
* 根据用户名称查询一个用户
* @param userName 根据用户名称查询一个用户
* @return 返回找到的用户
* */
public User findUserByUserName(String userName) {
User user=null;
try {
user=userName_Idx.get(userName);
} catch (DatabaseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return user;
}
/**
* 查询所有的用户
* @return 返回所有的用户
* */
public List<User> findAllUser() {
List<User> userList = new ArrayList<>();
//打开游标
EntityCursor<User> findEntityCursorUserList = null;
try {
//根据主键获取所有的对象集合中包含游标
findEntityCursorUserList = userId_Idx.entities();
for (User user : findEntityCursorUserList) {
userList.add(user);
}
} catch (DatabaseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
//关闭游标
findEntityCursorUserList.close();
}
return userList;
}
/**
* 根据email查找所有符合条件的用户
* 由于Email 和User关系我们设置的是多对一,即多个用户可以拥有相同的Email
* 那么当输入一个 Email 返回的User对象将可能是一个集合
* @param email 用户邮箱
* @return List<User> 查找到符合该邮箱的所有对象
**/
public List<User> findUserByEmail(String email) {
List<User> userList = new ArrayList<>();
EntityCursor<User> userEntityCursor=null;
try {
userEntityCursor= email_Idx.subIndex(email).entities();
for (User user : userEntityCursor) {
userList.add(user);
}
} catch (DatabaseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
//使用后必须关闭游标
userEntityCursor.close();
}
return userList;
}
/**
* 根据主键索引查询存储的用户总数
**/
public Long getAllUserCount() {
return userId_Idx.count();
}
/**
* 查询所有拥有这个email 的用户数量
**/
public Long getAllHaveTheEmailUserCount(String email) {
Long count=0L;
EntityCursor<User> userEntityCursor = null;
try {
userEntityCursor = email_Idx.subIndex(email).entities();
//设置游标到最后一个位置
userEntityCursor.last();
count=Long.valueOf(userEntityCursor.count());
} catch (DatabaseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
userEntityCursor.close();
}
return count;
}
}
4.5 主方法调用
import java.io.File;
import java.util.List;
import com.xingyun.dao.UserDA;
import com.xingyun.model.User;
import com.xingyun.util.BDBEnvironmentManager;
public class MainTest {
//指定数据库路径
private final static String BDB_ENV_HOME="bdb";
private final static File BDB_ENV_HOME_FILE=new File(BDB_ENV_HOME);
public static void main(String[] args) {
// TODO Auto-generated method stub
BDBEnvironmentManager.getInstance(BDB_ENV_HOME_FILE,false);
//数据访问层初始化
UserDA userDA=new UserDA(BDBEnvironmentManager.getMyEntityStore());
//添加数据
//创建一个User对象
User user=new User();
user.userName="zhangshan";
user.password="123456";
user.email="[email protected]";
//执行添加该User对象
userDA.addUser(user);
//再添加一个用户
User user2=new User();
user2.userName="lisi";
user2.password="123456";
user2.email="[email protected]";
//在添加一个用户
userDA.addUser(user2);
//再创建一个用户
User user3=new User();
user3.userName="wangwu";
user3.password="123456";
user3.email="[email protected]";
userDA.addUser(user3);
//获取查询用户数量
System.out.println("User have save Count="+userDA.getAllUserCount());
//查找拥有此邮箱的用户总数
System.out.println("User have [email protected] email count="+userDA.getAllHaveTheEmailUserCount("[email protected]"));
//直接查找所有的用户
List<User> findUserList=userDA.findAllUser();
for (User findUser : findUserList) {
System.out.println("----查找所有的用户---------");
System.out.println(findUser.userId);
System.out.println(findUser.userName);
System.out.println(findUser.password);
System.out.println(findUser.email);
System.out.println("----查找所有的用户---------");
}
//根据一级索引查找
System.out.println("---根据一级索引查找start----------");
User findUserById=userDA.findUserById(1L);
if(user!=null) {
System.out.println(findUserById.userId);
System.out.println(findUserById.userName);
System.out.println(findUserById.password);
System.out.println(findUserById.email);
}
System.out.println("----根据一级索引查找end---------");
//根据二级索引用户账号查找
System.out.println("-----根据二级索引用户账号查找start--------");
User findUserByUserName=userDA.findUserByUserName("zhangshan");
if(findUserByUserName!=null) {
System.out.println("-----------");
System.out.println(findUserByUserName.userId);
System.out.println(findUserByUserName.userName);
System.out.println(findUserByUserName.password);
System.out.println(findUserByUserName.email);
System.out.println("-------------");
}
System.out.println("-----根据二级索引用户账号查找end--------");
//根据二级索引Email查找
System.out.println("-----根据二级索引Email查找Start--------");
List<User> findUserByEmailList=userDA.findUserByEmail("[email protected]");
for (User findUser : findUserByEmailList) {
System.out.println("----------");
System.out.println(findUser.userId);
System.out.println(findUser.userName);
System.out.println(findUser.password);
System.out.println(findUser.email);
System.out.println("----------");
}
System.out.println("-----根据二级索引Email查找End-------");
}
}
输出结果如下:
User have save Count=3
User have [email protected] email count=2
----查找所有的用户---------
1
zhangshan
123456
[email protected]
----查找所有的用户---------
----查找所有的用户---------
2
lisi
123456
[email protected]
----查找所有的用户---------
----查找所有的用户---------
3
wangwu
123456
[email protected]
----查找所有的用户---------
---根据一级索引查找start----------
1
zhangshan
123456
[email protected]
----根据一级索引查找end---------
-----根据二级索引用户账号查找start--------
-----------
1
zhangshan
123456
[email protected]
-------------
-----根据二级索引用户账号查找end--------
-----根据二级索引Email查找Start--------
----------
1
zhangshan
123456
[email protected]
----------
----------
2
lisi
123456
[email protected]
----------
-----根据二级索引Email查找End-------
代码中有详细注释,不懂的地方或哪里有不对之处,欢迎留言区参与讨论