4.Think in Berkeley Database Java Edition 之增删改查实战

上篇博文其实我们已经对代码做了一定的封装,但是这远远还不够,你有没有发现上篇博文调用的时候还要用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 官网快速下载

Berkeley DB Java Edition (JE)

打开后会看到这个页面,点击下图中所示进行下载即可
BDB JE 下载
然后解压后复制lib文件夹下的jar 到自己的项目中
这里写图片描述

4.1.3 CSDN 赞助下载

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-------

代码中有详细注释,不懂的地方或哪里有不对之处,欢迎留言区参与讨论

猜你喜欢

转载自blog.csdn.net/hadues/article/details/81099140