文本数据库的简单java实现

这个是一个文本数据库简单的实现思路, 从这里可以下载到完整的java代码工程:  http://download.csdn.net/detail/hejiangtao/3991735

什么人适合阅读本文:

1. 我自己平时写些小程序,存储数据使用那些收钱/开源的数据库太浪费电脑资源,直接存到文件里面更方便. 所以才搞了这个东东, 如果你和我有同样的苦恼和需求,你可以参考下,看有木有你需要的. 我简单把实现思路写了一下,需要的兄弟也可以参考下,根据情况自己做扩展或者封装微笑.

2.其中使用到了java泛型,java反射机制,文本输入输出等技术,如果想研究这些技术也可以参考这些代码,,看有么有你需要的.

   设计思路说明:

1.数据存相关规则

一般的数据库的存储规则是不暴露给用户的,如果使用文本来存储数据,用户可以随便修改,所以必须建立一定的规则,用户不能随意手动修改文本数据库内容. 我实现的时候是一个表,一个文本文件; 一个表对应一个Bean class, 并假设,Bean Class的名字就是文件名加Bean后缀, 列名和TableBean的域(Field)名是一样,这样可以轻松使用java反射机制; 文本文件的第一行有效列需指明列名和顺序,因为Field的顺序是不可控的,存在文本里面就可控了; 第一列为主键,列与列之间使用$_$分割.

2. 关键算法/设计

1)读写文件--数据肯定是多行的,所以使用buffered reader 和writer是必要的, 写入和读出数据的时候都按照一条记录一行的方式,方便解析

2)文本数据和Bean之间的转化--将数据存入Bean是为了更方便的解析和使用, 由于Bean的Class Name和Field Name及其Set/Get方法都是采用同样的命名方式,所以可以通过使用一个java泛型方法实现所有表/Bean的文本数据和Bean之间的转化,访问数据也可以java反射机制访问.

3)数据分析--由于文件内容分析只能在内存里面分析,如果数据量太大,只能先一部分一本的分析,将分析完的数据先写入到临时文件, 本例作为demo就按照全部写入内存方式来分析

 实现:

 整个工程的文件分布如下如图:

1.在工程主目录下的db下面是两个示例表的文件T_FamilyMember.db, T_Home.db, 后缀名是.db其实是文本

2.在包名com.ross.filedb下面, tablebean下是两张表对应的Bean,命名必须符合规则; util下是一些公共的业务不相关的一些操作实现或系统级变量; FileDBTool.java则实现了基本的增/删/改/查功能.

3. junit.ross.filedb下是简单的junit功能测试用例 (木有main,看运行结果请用junit看偷笑)

工程种的文件分布

  先把Bean和db文件贴出来,命名是相互对应的,很明显,就不浪费口舌了:

T_Home.db:

#Column Sequence
ColumnSeq: id$_$name$_$phone$_$email$_$address
#Content Start
T_FamilyMember.db:

#Column Sequence
ColumnSeq: id$_$name$_$gender$_$mobile$_$email$_$address$_$family_id
#content start
T_HomeBean.java:

package com.ross.filedb.tablebean;
/**
 * Author: Jiangtao He
 * Since: MyJavaExpert v1.0
 */
public class T_HomeBean
{
    private String id;
    private String name;
    private String phone;
    private String email;
    private String address;

    //省略了set/get方法
}
T_FamilyMemberBean.java:

package com.ross.filedb.tablebean;
/**
 * Author: Jiangtao He; Email: ross.jiangtao.he@gmail.com
 * Since: MyJavaExpert v1.0
 */
public class T_FamilyMemberBean
{
    private String id;
    private String name;
    private String gender;
    private String mobile;
    private String email;
    private String address;
    private String family_id;
    //省略了set/get方法
}


再介绍下我的通用方法和系统级变量:

          SysValues.java:

系统及变量,主要是在使用java反射机制的时候分析类名或者文件名.

package com.ross.filedb.util;
/**
 * Author: Jiangtao He; Email: ross.jiangtao.he@gmail.com
 * Date: 2011-12-30
 * Version: MyJavaExpert v1.0
 * Description: system level variables
 */
public class SysValues
{
    // DB constant variables
    public static final String DB_Path = System.getProperty("user.dir")
            + System.getProperty("file.separator") + "db"
            + System.getProperty("file.separator");
    public static final String DB_Bean_Suffix = "Bean";
    public static final String DB_File_Suffix = ".db";

}
ResultBean.java:

用于一般的方法返回结果.

package com.ross.filedb.util;
/**
 * Author: Jiangtao He; Email: ross.jiangtao.he@gmail.com
 * Date: 2011-12-31
 * Since: MyJavaExpert v1.0
 * Description: To store the return value.
 */
public class ResultBean
{
    boolean result;
    String description;
   //省略了set/get方法
}
FileProcess.java

java IO的操作,总要一堆的对象相互嵌套才能生成合适的writer或者reader, 这个类的作用就是把这个步骤汇总,其他类可以直接引用,省去这些繁琐的麻烦. 具体功能,代码里有详细注释.

package com.ross.filedb.util;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
/**
 * Author: Jiangtao He; Email: ross.jiangtao.he@gmail.com
 * Since: MyJavaExpert v1.0
 * Description: this class will implement the common file process.
 */
public class FileProcess
{
    /**
     * Description: get a buffered file append writer according the file name (with full path)
     * PrintWriter will be more easy to access text, BufferedWriter is used to 
     * cache the data to improve the performance.
     */
    public PrintWriter getAppendBufferedFilePrintWriter(String sFullFileName)
            throws IOException
    {
        PrintWriter oPWriter = new PrintWriter(new BufferedWriter(
                new FileWriter(sFullFileName, true)));
        return oPWriter;
    }

    /**
     * Author: Jiangtao He
     * Description: get a buffered file writer according the file name (with full path)
     * PrintWriter will be more easy to access text, BufferedWriter is used to 
     * cache the data to improve the performance.
     */
    public PrintWriter getBufferedFilePrintWriter(String sFullFileName)
            throws IOException
    {
        PrintWriter oPWriter = new PrintWriter(new BufferedWriter(
                new FileWriter(sFullFileName)));
        return oPWriter;
    }
    
    /**
     * Author: Jiangtao He
     * Description: get a buffered file reader according the file name (with full path)
     * BufferedReader is used to cache the data to improve the performance.
     */
    public BufferedReader getBufferedFileReader(String sFullFileName)
            throws IOException
    {
        BufferedReader oBufferedReader = new BufferedReader(new FileReader(
                sFullFileName));
        return oBufferedReader;
    }
}


最后就是我们的增删改查功能实现了:

FileDBTool.java:

1. 看下如何根据bean类获取对应的文本数据库的表文件名. 

a.首先获取完整类名,例如 com.ross.filedb.tablebean.T_HomeBean;

b.然后去除包名,只剩T_HomeBean; 

c.再将Bean后缀去掉,加上路径和.db后缀,就将文本数据库的表文件名给拼出来了.

/**
     * Author: Jiangtao He; Email: ross.jiangtao.he@gmail.com
     * Description: Get the text DB's file name with full path name.
     * assume that all the bean class of table is formed by 
     * table name + suffix (like: Bean), for example: T_Home-->T_HomeBean.java
     * @param oT: the object of bean 
     * @return sFullFileName: db file name with completing path
     */
    private <T> String getFullTableFileName(T oT)
    {
        String sFullFileName = "";

        // the class name is like com.ross.filedb.tablebean.T_HomeBean, only keep
        // T_Home
        String[] sTmps = oT.getClass().getName().split("\\.");
        sFullFileName = sTmps[sTmps.length - 1];

        sFullFileName = SysValues.DB_Path
                + sFullFileName.substring(0, sFullFileName
                        .indexOf(SysValues.DB_Bean_Suffix))
                + SysValues.DB_File_Suffix;
        return sFullFileName;
    }
2. 看下如何将Bean转换成一条表记录(使用"$_$"分割各列,各列需按照指定顺序排列)

a.首先根据上面的方法拼出来的带路径的文件名读出列名和各列顺序

b.然后根据列名拼出类的Field的get方法

c.再根据拼接出来的get方法,使用java的反射机制, 生成对应的Method对象, 并使用invoke方法执行该方法

d.使用循环拼接各个Field的值在一起,并使用"$_$"分割各个Field

另外,为了使所有的Bean都可以通过这一个方法转换成对应的字符串,将方法定义成了泛型方法, 可以看到泛型T.

不了解java反射和泛型的可以从网上搜一下,或者看我后续的博客了解 奋斗

/**
      * Author: Jiangtao He; Email: ross.jiangtao.he@gmail.com
      * Description: 
      * 1. Get the string of new record which will be save to DB
      * 2. Since the object is a generic object, so the reflect mechanism will be 
      *    used to access the data of the bean.
      * 3. assume that all fields'nameof the bean class is the same as 
      *    the columns' name of table. The first available line should
      *    give the columns' sequence, which is start with 'ColumnSeq'.
      *    for different columns, they will be separated by '$_$'
      * @param oT: the object of bean 
      * @param sFullFileName: DB file name with completing path
      * @return sRowData: return the new record string
      * @throws IOException 
      * @throws NoSuchMethodException 
      * @throws SecurityException 
      * @throws InvocationTargetException 
      * @throws IllegalAccessException 
      * @throws IllegalArgumentException 
      */

    private <T> String convertBeantoStr(T oT, String sFullFileName)
            throws IOException, SecurityException, NoSuchMethodException,
            IllegalArgumentException, IllegalAccessException,
            InvocationTargetException
    {
        // define the return value
        String sRowdata = "";

        String[] sColumns = null;
        BufferedReader oBRead = null;
        // to get the columns' sequence
        try
        {
            oBRead = fileProcess.getBufferedFileReader(sFullFileName);
            String sReadLine;
            while (null != (sReadLine = oBRead.readLine()))
            {
                sReadLine = sReadLine.trim();
                // skip the comments and empty lines
                if ("".equals(sReadLine) || sReadLine.startsWith("#"))
                {
                    continue;
                }

                // find the columns' sequence
                if (sReadLine.startsWith("ColumnSeq"))
                {
                    sColumns = sReadLine.split(":")[1].split("\\$_\\$");
                    break;
                }
            }
        }
        catch (IOException e)
        {
            // print e, and throw it, so the method which invoked it can process
            // it freely.
            e.printStackTrace();
            throw e;
        }
        finally
        {
            // close the writer
            if (null != oBRead)
            {
                oBRead.close();
            }
        }

        // define field variable
        Method oMethod = null;
        String sFieldName = "";
        String sMethodName = "";
        // get the corresponding columns' value through the reflect mechanism
        for (int i = 0; i < sColumns.length; i++)
        {
            // initialize the method name
            sMethodName = "get";


            // get the get method name of the current column
            sFieldName = sColumns[i].trim();
            if (sFieldName.length() > 1)
            {
                sMethodName = sMethodName
                        + sFieldName.substring(0, 1).toUpperCase()
                        + sFieldName.substring(1);
            }
            else
            {
                sMethodName = sMethodName + sFieldName.toUpperCase();
            }
            // get the get method
            oMethod = oT.getClass().getMethod(sMethodName);
            // Execute the method to get the value of the current field
            // add the separator of the columns too
            sRowdata = sRowdata + oMethod.invoke(oT) + "$_$";
        }
        // remove the last separator
        if (!"".equals(sRowdata))
        {
            sRowdata = sRowdata.substring(0, sRowdata.lastIndexOf("$_$"));
        }
        return sRowdata;
    }

3.有了上面两个步骤,就可以实现数据库记录的插入功能了

a. 这个insert方法的传入参数就是一个Table Bean的对象, 当然对象里面有你要存储到文本文件里的数据

b.根据这个Table Bean的对象获取带路径的完整文件名

c.将Bean转化成一条合格的字符串

d.将该字符串写入对应的DB文件的最后(为了保证数据在单独的一行,数据前先插入了换行符)

同样的,为了使所有的表Bean都可以通过这一个方法插入数据,将方法定义成了泛型方法. 

本方法并没有实现检查主键的工作,自己可以根据需要重载或封装实现.

/**
     * Author: Jiangtao He; Email: ross.jiangtao.he@gmail.com
     * Description: Insert one record to text DB
     * @param oT: the object of bean 
     * @return oResult: 
     *          ResultBean.result: true - insert success; false - insert failed
     *          ResultBean.description: the description of the result;
     * @throws IOException 
     * @throws InvocationTargetException 
     * @throws IllegalAccessException 
     * @throws NoSuchMethodException 
     * @throws IllegalArgumentException 
     * @throws SecurityException 
     */
    public <T> ResultBean insert(T oT) throws IOException, SecurityException,
            IllegalArgumentException, NoSuchMethodException,
            IllegalAccessException, InvocationTargetException
    {
        ResultBean oResult = new ResultBean();

        boolean bRet = false;
        String sDescription = "";

        // get full text DB file name with full path
        String sFullFileName = this.getFullTableFileName(oT);

        // get sRowdata
        String sRowData = this.convertBeantoStr(oT, sFullFileName);

        PrintWriter oPWriter = null;
        try
        {
            // get writer
            oPWriter = fileProcess
                    .getAppendBufferedFilePrintWriter(sFullFileName);
            // write to file
            oPWriter.write(System.getProperty("line.separator") + sRowData);
            oPWriter.flush();

            // set return value as success
            bRet = true;
            sDescription = "the insert record is \"" + sRowData + "\". "
                    + ". It is saved in \"" + sFullFileName
                    + "\" successfully.";
        }
        catch (IOException e)
        {
            e.printStackTrace();
            throw e;
        }
        finally
        {
            // close the writer
            if (null != oPWriter)
            {
                oPWriter.close();
            }
        }
        // set final result.
        oResult.setDescription(sDescription);
        oResult.setResult(bRet);

        return oResult;
    }

4. 先了解下如何将一条记录从文本数据库表文件中删除

java没有删除文本中某一行的接口,所以实现删除的算法就是,将文本内容全部读出来,将改行从缓存中删除,然后将处理后的缓存数据覆盖写入原文件. 缓存方式有两种一种全部存入内存适用于数据较少的时候,另一种是写入临时文件,适用于大数量时候.本demo全部读入内存了.

a. 获取对应DB文件的reader

b.然后一行一行的读取,一行一行的判断是否当前行就是要删除的行,如果不是则加入缓存的字符串中,否则丢弃

c.将处理后的缓存字符串写入对应的DB文件

/**
     * Author: Jiangtao He; Email: ross.jiangtao.he@gmail.com
     * Description: 
     * 1.delete the record from text DB file. 
     * 2.the arithmetic is read all the contents in  a string buffer except 
     *   the deleting record, then write to file again.
     * 3.One suggestion for huge data, develop your own method by using a  temporary file
     *   as a buffered
     * @param sRowData: the record which will be deleted from the file
     * @param sFullFileName: DB file name with completing path
     * @return bRet: true - insert success; false - insert failed
     * @throws IOException 
     */
    private boolean deleteRecordFromFile(String sRowData, String sFullFileName)
            throws IOException
    {
        boolean bRet = false;

        StringBuffer sbFinalContent = new StringBuffer();

        PrintWriter oPWriter = null;
        BufferedReader oBRead = null;
        // to get the columns' sequence
        try
        {
            oBRead = fileProcess.getBufferedFileReader(sFullFileName);
            String sReadLine;
            String sLastLIne = "sLastLIne";
            while (null != (sReadLine = oBRead.readLine()))
            {
                sReadLine = sReadLine.trim();

                // make conjoined duplicated lines to one line
                // to avoid too many empty lines
                if (sLastLIne.equals(sReadLine))
                {
                    continue;
                }
                // delete the target record
                if (sRowData.equals(sReadLine))
                {
                 // set result
                    bRet = true;                    
                    continue;
                }
                // cache in the buffer
                sbFinalContent.append(sReadLine).append(
                        System.getProperty("line.separator"));
                // set last line
                sLastLIne = sReadLine;
            }

            // get writer
            oPWriter = fileProcess
                    .getBufferedFilePrintWriter(sFullFileName);

            // write the final content to file
            oPWriter.write(sbFinalContent.toString());
            oPWriter.flush();
        }
        catch (IOException e)
        {
            // print e, and throw it, so the method which invoked it can process
            // it freely.
            e.printStackTrace();
            throw e;
        }
        finally
        {
            if (null != oBRead)
            {
                oBRead.close();
            }
            // close the writer
            if (null != oPWriter)
            {
                oPWriter.close();
            }
        }
        return bRet;
    }

5. 有了上面的方法,我们就可以实现数据的删除功能了.

我们平时用的数据库可以使用主键或者其他字段作为条件来做删除, 这个在java里面就是多了一步字符串的分析操作.所以我没有实现这个功能, 在本demo中就是直接将整条记录作为判断条件来删除数据, 可以根据实际需要扩展本方法.

a.首先根据表的Bean对象获取文件名

b.然后将Bean对象转换为合格的表记录字符串

c.调用上面的方法将记录从文件中删除.

同样的, 为了使所有的表Bean都可以通过这一个方法删除数据,将方法定义成了泛型方法. 

 /**
     * Author: Jiangtao He; Email: ross.jiangtao.he@gmail.com
     * Description: Delete one record from text DB
     * @param oT: the object of bean 
     * @return oResult: 
     *          ResultBean.result: true - insert success; false - insert failed
     *          ResultBean.description: the description of the result;
     * @throws InvocationTargetException 
     * @throws IllegalAccessException 
     * @throws NoSuchMethodException 
     * @throws IOException 
     * @throws IllegalArgumentException 
     * @throws SecurityException 
     */
    public <T> ResultBean delete(T oT) throws SecurityException,
            IllegalArgumentException, IOException, NoSuchMethodException,
            IllegalAccessException, InvocationTargetException
    {
        ResultBean oResult = new ResultBean();

        boolean bRet = false;
        String sDescription = "";

        // get full text DB file name with full path
        String sFullFileName = this.getFullTableFileName(oT);


        // get sRowdata
        String sRowData = this.convertBeantoStr(oT, sFullFileName);

        // delete from file
        bRet = this.deleteRecordFromFile(sRowData, sFullFileName);

        // set result description
        if (bRet)
        {
            sDescription = "The deleted record is \"" + sRowData + "\" . "
                    + "It is delete from \"" + sFullFileName
                    + "\" successfully.";
        }
        else
        {
            sDescription = "The deleting record \"" + sRowData
                    + "\" is not there in the file" + " \"" + sFullFileName
                    + "\".";
        }
        // set final result.
        oResult.setDescription(sDescription);
        oResult.setResult(bRet);

        return oResult;
    }
6.再看下如何更新文本数据库中的一条记录

更新记录算法和删除记录是一样的,只不过删除时丢弃该记录, 更新时更换该记录, 不再赘述.

a. 获取对应DB文件的reader

b.然后一行一行的读取,一行一行的判断是否当前行就是要删除的行,如果不是则加入缓存的字符串中,否则增更换为目标字符串

c.将处理后的缓存字符串写入对应的DB文件

/**
     * Author: Jiangtao He; Email: ross.jiangtao.he@gmail.com
     * Description: 
     * 1.update the record to text DB file. 
     * 2.the arithmetic is read all the contents in  a string buffer, replace 
     *   the updating record, then write to file again.
     * 3.One suggestion for huge data, develop your own method by using a  temporary file
     *   as a buffered
     * @param sRowData: the record which will be deleted from the file
     * @param sFullFileName: DB file name with completing path 
     * @param sPrimaryKeyValue: the value of the primary key
     * @return bRet: true - insert success; false - insert failed
     * @throws IOException 
     */
    private boolean updateRecordToFile(String sRowData,
            String sPrimaryKeyValue, String sFullFileName) throws IOException
    {
        boolean bRet = false;

        StringBuffer sbFinalContent = new StringBuffer();

        PrintWriter oPWriter = null;
        BufferedReader oBRead = null;

        try
        {
            oBRead = fileProcess.getBufferedFileReader(sFullFileName);
            String sReadLine;
            String sLastLIne = "sLastLIne";
            String[] sColumnValues = null;
            while (null != (sReadLine = oBRead.readLine()))
            {
                sReadLine = sReadLine.trim();

                // make conjoined duplicated lines to one line
                // to avoid too many empty lines
                if (sLastLIne.equals(sReadLine))
                {
                    continue;
                }

                // handle the comments, empty line, ColumnSeq line
                if (sReadLine.startsWith("#") || "".equals(sReadLine)
                        || sReadLine.startsWith("ColumnSeq"))
                {
                    // cache in the buffer
                    sbFinalContent.append(sReadLine).append(
                            System.getProperty("line.separator"));
                    continue;
                }

                // handle changing record
                sColumnValues = sReadLine.split("\\$_\\$");
                if (sPrimaryKeyValue.trim().equals(sColumnValues[0].trim()))
                {
                    // cache in the buffer
                    sbFinalContent.append(sRowData).append(
                            System.getProperty("line.separator"));
                    continue;
                }

                // cache normal record in the buffer
                sbFinalContent.append(sReadLine).append(
                        System.getProperty("line.separator"));

                // set last line
                sLastLIne = sReadLine;
            }

            // get writer
            oPWriter = fileProcess.getBufferedFilePrintWriter(sFullFileName);

            // write the final content to file
            oPWriter.write(sbFinalContent.toString());
            oPWriter.flush();

            // set result
            bRet = true;
        }
        catch (IOException e)
        {
            // print e, and throw it, so the method which invoked it can process
            // it freely.
            e.printStackTrace();
            throw e;
        }
        finally
        {
            if (null != oBRead)
            {
                oBRead.close();
            }
            // close the writer
            if (null != oPWriter)
            {
                oPWriter.close();
            }
        }
        return bRet;
    }

7. 通过上面的更新方法,就可以实现记录的更新功能了.

同样的对与更新方法没有实现根据列值判断来更新相关的记录, 只是实现了根据主键更新整列数据局, 可以根据需要重写/扩展该方法

a.首先根据Table Bean后去带路径的玩这个路径名

b.在将Table Bean转换成一个合格的数据记录字符串

c.调用上面的方法,根据主键将目标记录替换

同样的,为了使所有的表Bean都可以通过这一个方更新数据,将方法定义成了泛型方法. 

/**
     * Author: Jiangtao He; Email: ross.jiangtao.he@gmail.com
     * Description: update one record from text DB
     * @param oT: the object of bean 
     * @param sPrimaryKeyValue: the value of the primary key
     * @return oResult: 
     *          ResultBean.result: true - insert success; false - insert failed
     *          ResultBean.description: the description of the result;
     * @throws InvocationTargetException 
     * @throws IllegalAccessException 
     * @throws NoSuchMethodException 
     * @throws IOException 
     * @throws IllegalArgumentException 
     * @throws SecurityException 
     */
    public <T> ResultBean update(T oT, String sPrimaryKeyValue)
            throws SecurityException, IllegalArgumentException, IOException,
            NoSuchMethodException, IllegalAccessException,
            InvocationTargetException
    {
        ResultBean oResult = new ResultBean();

        boolean bRet = false;
        String sDescription = "";

        // get full text DB file name with full path
        String sFullFileName = this.getFullTableFileName(oT);

        // get sRowdata
        String sRowData = this.convertBeantoStr(oT, sFullFileName);

        // delete from file
        bRet = this.updateRecordToFile(sRowData, sPrimaryKeyValue,
                sFullFileName);

        // set result description
        if (bRet)
        {
            sDescription = "The updated record is \"" + sRowData + "\". "
                    + " It is update to \"" + sFullFileName
                    + "\" successfully.";
        }
        else
        {
            sDescription = "The updating record \"" + sRowData
                    + "\" is not there in the file" + " \"" + sFullFileName
                    + "\".";
        }
        // set final result.
        oResult.setDescription(sDescription);
        oResult.setResult(bRet);
        return oResult;
    }
8. 了解下如何根据列名获取Table Bean的get 方法

由于列名和Bean的Field名字是一样的,根据这儿特性,可以拼接出来对应的set方法的方法名. 详细步骤可以参考注释

同样的, 为了使所有的表Bean都可以通过这一个方法获取set方法,将方法定义成了泛型方法. 

/**
     * Author: Jiangtao He; Email: ross.jiangtao.he@gmail.com
     * Description: Get all set method names.
     * assume that all the field names of bean class of table are the same as the column names
     * @param oT: the object of bean 
     * @param sColumns: the columns
     * @return HashMap <String,String> 
     *           - key: column name;  
     *           - value: the corresponding set method name
     */
    private <T> HashMap<String, String> getSetMethods(String[] sColumns, T oT)
    {
        HashMap<String, String> oHM = new HashMap<String, String>();
        String sMethodName = "";
        String sFieldName = "";
        for (int i = 0; i < sColumns.length; i++)
        {
            // initialize the method name
            sMethodName = "set";

            // get the get method name of the current column
            sFieldName = sColumns[i].trim();
            if (sFieldName.length() > 1)
            {
                sMethodName = sMethodName
                        + sFieldName.substring(0, 1).toUpperCase()
                        + sFieldName.substring(1);
            }
            else
            {
                sMethodName = sMethodName + sFieldName.toUpperCase();
            }
            oHM.put(sColumns[i], sMethodName);
        }
        return oHM;
    }

9. 有了上面的方法,我们也可以更加easy的实现数据的查询功能了.

为了免去分析字符串的麻烦,本demo只是先了查询所有数据的功能,可以根据需要扩展该方法.

这个方法设计是将所有数据读取出来放入Bean中,一条记录就是一个Bean对象,所以最终返回值是一个Bean对象的list

a.首先获取对应的带路径的完整文件名

b.获取对应数据库文件的reader

c.将数据一条一条的读出来,一条一条的处理. 跳过注释,空行,及标示列名和列顺序的行, 将其他有效的数据库记录进行处理

d.根据列名得出所有field的set方法

c.将数据库记录分割后,调用set方法赋给Bean对象

d.将赋值完后的Bean对象加入List, 如此循环处理所有记录.

同样的,为了使所有的表Bean都可以通过这一个方法获取对应的数据列表,将方法定义成了泛型方法. 

另外注意一点, 由于set方法是有输入参数的,所以使用反射机制构造或者执行方法的时候是需要指定这些参数类型或者参数的, 由于demo中的数据类型都是使用的String类型,所以是取巧了的, 如果需要支持其他类型,可以对此方法做修改,对类型做下分析.

/**
     * Author: Jiangtao He; Email: ross.jiangtao.he@gmail.com
     * Description: query all the records from text DB
     * @param oT: the object of bean 
     * @param sPrimaryKeyValue: the value of the primary key
     * @return oTList: the object list of the bean
     * @throws InvocationTargetException 
     * @throws IllegalAccessException 
     * @throws IllegalArgumentException 
     * @throws NoSuchMethodException 
     * @throws SecurityException 
     * @throws InstantiationException 
     * @throws IOException 
     */
    public <T> List<T> queryAll(T oT) throws IllegalArgumentException,
            IllegalAccessException, InvocationTargetException,
            SecurityException, NoSuchMethodException, InstantiationException, IOException
    {
        List<T> oTList = new ArrayList<T>();

        // get full text DB file name with full path
        String sFullFileName = this.getFullTableFileName(oT);

        BufferedReader oBRead = null;
        try
        {
            oBRead = fileProcess.getBufferedFileReader(sFullFileName);
            String sReadLine;
            String[] sColumns = null;
            HashMap<String, String> oHM = null;
            String[] sColumnValues = null;
            int iNumOfColumns;
            Method oMethod;
            while (null != (sReadLine = oBRead.readLine()))
            {
                sReadLine = sReadLine.trim();

                // skip comments, empty line
                if (sReadLine.startsWith("#") || "".equals(sReadLine))
                {
                    continue;
                }

                // get set method names of the bean
                if (sReadLine.startsWith("ColumnSeq"))
                {
                    sColumns = sReadLine.split(":")[1].split("\\$_\\$");
                    oHM = this.getSetMethods(sColumns, oT);
                    continue;
                }

                // get values of the a record
                sColumnValues = sReadLine.split("\\$_\\$");

                // check the number of values is less than columns or not, the
                // smaller one will be used in the loop to avoid out of array
                // index exception
                iNumOfColumns = sColumnValues.length > sColumns.length ? sColumns.length
                        : sColumnValues.length;
                for (int i = 0; i < iNumOfColumns; i++)
                {
                    // get the get method
                    oMethod = oT.getClass().getMethod(oHM.get(sColumns[i]),String.class);
                    // execute the method to set the value into bean
                    oMethod.invoke(oT, sColumnValues[i]);
                }
                //add the bean into the bean list
                oTList.add(oT);
                //assign a new instance to the bean
                oT = (T) oT.getClass().newInstance();
            }
        }
        catch (IOException e)
        {
            // print e, and throw it, so the method which invoked it can process
            // it freely.
            e.printStackTrace();
            throw e;
        }
        finally
        {
            if (null != oBRead)
            {
                oBRead.close();
            }
        }
        return oTList;
    }


 至此增删改查功能的思路算是实现了.


我们使用junit做下测试看下效果--我在4个测试方法上都打了断点, 分别看下实际效果(由于junit代码较长,我就不贴了,感兴趣的可以下载下来看):

增加一条记录到home和member表:

控制台:

Insert one home record - result: true; description: the insert record is "Ross_1$_$Ross's Home$_$252363693$_$ross.jiangtao.he@gmail.com$_$China P.R.". . It is saved in "E:\myspace\MyJavaExpert V1.0\db\T_Home.db" successfully.
Insert one faminily member record - result: true; description: the insert record is "Yan$_$Pery$_$Female$_$252363693$_$ross.jiangtao.he@gmail.com$_$null$_$Ross_1". . It is saved in "E:\myspace\MyJavaExpert V1.0\db\T_FamilyMember.db" successfully.

数据库文件内容:

T_Home.db:

#Column Sequence
ColumnSeq: id$_$name$_$phone$_$email$_$address

#Content Start
Ross_1$_$Ross's Home$_$252363693$_$ross.jiangtao.he@gmail.com$_$China P.R.
T_FamilyMember.db:

#Column Sequence
ColumnSeq: id$_$name$_$gender$_$mobile$_$email$_$address$_$family_id

#content start
Yan$_$Pery$_$Female$_$252363693$_$ross.jiangtao.he@gmail.com$_$null$_$Ross_1

查询home和member表的记录:
控制台:

Query all the home record(s) from file, totally get : 1 record(s)
Query all the family member record(s) from file, totally get : 1 record(s)

更新home和member表的记录(可以看多了"O(∩_∩)O~~"的列) 控制台:

Update one faminily member record - result: true; description: The updated record is "Yan$_$Pery$_$Female$_$10086 O(∩_∩)O~~$_$ross.jiangtao.he@gmail.com$_$null$_$Ross_1".  It is update to "E:\myspace\MyJavaExpert V1.0\db\T_FamilyMember.db" successfully.
Update one home record - result: true; description: The updated record is "Ross_1$_$Pery's Home O(∩_∩)O~~$_$252363693$_$ross.jiangtao.he@gmail.com$_$China P.R.".  It is update to "E:\myspace\MyJavaExpert V1.0\db\T_Home.db" successfully.
T_Home.db:

#Column Sequence
ColumnSeq: id$_$name$_$phone$_$email$_$address
#Content Start
Ross_1$_$Pery's Home O(∩_∩)O~~$_$252363693$_$ross.jiangtao.he@gmail.com$_$China P.R.

T_FamilyMember.db:

#Column Sequence
ColumnSeq: id$_$name$_$gender$_$mobile$_$email$_$address$_$family_id
#content start
Yan$_$Pery$_$Female$_$10086 O(∩_∩)O~~$_$ross.jiangtao.he@gmail.com$_$null$_$Ross_1

删除home和member表的一条记录

控制台:
Delete one faminily member record - result: true; description: The deleted record is "Yan$_$Pery$_$Female$_$10086 O(∩_∩)O~~$_$ross.jiangtao.he@gmail.com$_$null$_$Ross_1" . It is delete from "E:\myspace\MyJavaExpert V1.0\db\T_FamilyMember.db" successfully.
Delete one home record - result: true; description: The deleted record is "Ross_1$_$Pery's Home O(∩_∩)O~~$_$252363693$_$ross.jiangtao.he@gmail.com$_$China P.R." . It is delete from "E:\myspace\MyJavaExpert V1.0\db\T_Home.db" successfully.
T_Home.db:
#Column Sequence
ColumnSeq: id$_$name$_$phone$_$email$_$address
#Content Start

T_FamilyMember.db:

#Column Sequence
ColumnSeq: id$_$name$_$gender$_$mobile$_$email$_$address$_$family_id
#content start










 至此"文本数据库简单的实现思路"已经完成了.

注: 转载请注明出处: 注: 转载请注明出处: http://hejiangtao.iteye.com/, 用于商业得给我分成大笑

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值