JavaDBF

repository·main·Indexed 42 days ago

https://github.com/albfernandez/javadbf

A Java library for reading and writing XBase (.dbf) files, primarily used for data transfer with legacy applications. It supports standard XBase and FoxPro specific data types, mapping them to Java types like String, BigDecimal, and Date. The library includes a DBFReader for accessing data by index or field name and a DBFWriter that supports a 'Sync Mode' for handling large datasets without OutOfMemoryErrors. Note that JavaDBF is not thread-safe and does not implement the JDBC API.

Tokens
3.5K
Snippets
12
Records
14
Agent score
4%

What's inside JavaDBF

  1. Core Concepts of JavaDBF

    main

    JavaDBF is a Java library for reading and writing XBase (.dbf) files.

    Key Architectural Notes:

    • Non-JDBC: It does not implement the JDBC API and is not intended for full-blown RDBMS-style database interactions or as a backend.
    • Thread Safety: The library is not thread-safe. You must manage concurrency when using it in multithreaded applications.
    • Package: All classes are located in the com.linuxense.javadbf package.
  2. Use Sync Mode for large DBF files

    main

    Standard DBFWriter usage keeps all records in memory until write() is called, which can cause memory issues with large datasets.

    To use Sync Mode, instantiate DBFWriter with a File object representing a new or empty file. In this mode, records are written to the file immediately when addRecord() is called. You must still call write() at the end to ensure metadata is correctly written.

    // This DBFWriter object is now in Sync Mode
    DBFWriter writer = new DBFWriter( new File( "/path/to/a/new/file"));
    // ... add records ...
    writer.write(someOutputStream);
  3. Use Sync Mode for writing large DBF files

    main

    To avoid OutOfMemoryError when writing massive amounts of data, use Sync Mode. In this mode, records are written to the file immediately when addRecord() is called, rather than being buffered in memory.

    To enable Sync Mode, instantiate DBFWriter by passing a File object (representing a new or empty file) instead of an OutputStream.

    import com.linuxense.javadbf.*;
    import java.io.*;
    
    // ...
    // Passing a File object instead of an OutputStream enables Sync Mode
    DBFWriter writer = new DBFWriter(new File("/path/to/a/new/file")); 
    
    writer.setFields(fields);
    writer.addRecord(rowData);
    writer.close();
    // ...
  4. Write a DBF file using DBFWriter

    main

    To write a DBF file, follow these steps:

    1. Define Fields: Create DBFField objects, set their name, type (using DBFDataType), and length. For numeric types, you can also set setDecimalCount(int).
    2. Initialize Writer: Create a DBFWriter with an OutputStream and call setFields(DBFField[] fields).
    3. Add Records: Use addRecord(Object[] rowData) where the array elements correspond to the defined fields.
    4. Close: Call close() to finalize the file and write metadata.

    Warning: In standard mode, all records are kept in memory until close() is called. For very large datasets, use Sync Mode (see below).

    import com.linuxense.javadbf.*;
    import java.io.*;
    
    public class JavaDBFWriterTest {
    	public static void main(String args[]) throws IOException {
    		DBFField[] fields = new DBFField[3];
    
    		fields[0] = new DBFField();
    		fields[0].setName("emp_code");
    		fields[0].setType(DBFDataType.CHARACTER);
    		fields[0].setLength(10);
    
    		fields[1] = new DBFField();
    		fields[1].setName("emp_name");
    		fields[1].setType(DBFDataType.CHARACTER);
    		fields[1].setLength(20);
    
    		fields[2] = new DBFField();
    		fields[2].setName("salary");
    		fields[2].setType(DBFDataType.NUMERIC);
    		fields[2].setLength(12);
    		fields[2].setDecimalCount(2);
    
    		DBFWriter writer = new DBFWriter(new FileOutputStream(args[0]));
    		writer.setFields(fields);
    
    		Object rowData[] = new Object[3];
    		rowData[0] = "1000";
    		rowData[1] = "John";
    		rowData[2] = new Double(5000.00);
    		writer.addRecord(rowData);
    
    		writer.close();
    	}
    }
  5. Install JavaDBF via Maven

    main

    If you are using Maven, add the following dependency to your pom.xml to include JavaDBF in your project.

    <dependency>
    	<groupId>com.github.albfernandez</groupId>
    	<artifactId>javadbf</artifactId>
    	<version>1.13.1</version>
    </dependency>
  6. Append records to an existing DBF file

    main

    Starting from version 0.4.0, JavaDBF supports appending records to an existing DBF file. To do this, instantiate DBFWriter using a File object that points to the existing DBF file. This puts the writer into 'Sync Mode'.

    Important Constraints:

    • You must not call setFields() on a DBFWriter instance used for appending, as the schema is already defined by the existing file.
    • You must call the close() method after adding all records to ensure the data is properly flushed and the file is finalized.
    import com.linuxense.javadbf.*;
    import java.io.*;
    
    public class DBFWriterTest {
    
      public static void main(String args[]) throws IOException {
    
        // Initialize writer in Sync Mode by passing an existing file
        DBFWriter writer = new DBFWriter(new File("/path/to/an/existing/dbfile")); 
        
        // Add the data (do NOT call setFields)
        writer.addRecord(rowData);
        
        // Finalize the file
        writer.close();
      }
    }
  7. Build JavaDBF from source

    main

    To build the project from the source code, clone the repository and use Maven to package the JAR file.

    1. Clone the repository.
    2. Navigate to the project root.
    3. Run the Maven clean package command.

    The resulting JAR file will be located at target/javadbf-x.xx.x.jar.

    git clone https://github.com/albfernandez/javadbf.git
    cd javadbf
    mvn clean package
  8. Read a DBF file using field names

    main

    From version 1.7.0 onwards, you can access data using field names instead of indices. To use this feature, you must use nextRow() instead of nextRecord(). The nextRow() method returns a DBFRow object.

    import java.io.*;
    import com.linuxense.javadbf.*;
    
    public class JavaDBFReaderWithFieldNamesTest {
    	public static void main(String args[]) {
    		DBFReader reader = null;
    		try {
    			reader = new DBFReader(new FileInputStream(args[0]));
    			// ... field inspection logic ...
    			DBFRow row;
    			while ((row = reader.nextRow()) != null) {
    				System.out.println(row.getString("PHONE"));
    			}
    		} catch (DBFException e) {
    			e.printStackTrace();
    		} catch (IOException e) {
    			e.printStackTrace();
    		} finally {
    			DBFUtils.close(reader);
    		}
    	}
    }
  9. Read Memo fields from an external file

    main

    If your DBF file uses an external memo file (e.g., .dbt or .fpt), you must specify it using reader.setMemoFile(File memoFile). If not specified, memo fields will be returned as null.

    import java.io.*;
    import com.linuxense.javadbf.*;
    
    public class JavaDBFReaderMemoTest {
    	public static void main(String args[]) {
    		DBFReader reader = null;
    		try {
    			reader = new DBFReader(new FileInputStream(args[0]));
    			reader.setMemoFile(new File("memo.dbt"));
    			// ... read data ...
    		} catch (DBFException e) {
    			e.printStackTrace();
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    	}
    }
  10. Read a DBF file using DBFReader

    main

    To read a DBF file, instantiate a DBFReader with an InputStream. You can iterate through fields using getFieldCount() and getField(i), and iterate through records using nextRecord(), which returns an Object[] for each row or null when finished.

    import java.io.*;
    import com.linuxense.javadbf.*;
    
    public class JavaDBFReaderTest {
      public static void main( String args[]) {
        try {
          InputStream inputStream  = new FileInputStream( args[ 0]);
          DBFReader reader = new DBFReader( inputStream);
    
          int numberOfFields = reader.getFieldCount();
          for( int i=0; i<numberOfFields; i++) {
            DBFField field = reader.getField( i);
            System.out.println( field.getName());
          }
    
          Object []rowObjects;
          while( (rowObjects = reader.nextRecord()) != null) {
            for( int i=0; i<rowObjects.length; i++) {
              System.out.println( rowObjects[i]);
            }
          }
          inputStream.close();
        }
        catch( DBFException e) {
          System.out.println( e.getMessage());
        }
        catch( IOException e) {
          System.out.println( e.getMessage());
        }
      }
    }
  11. Define DBF fields using DBFField

    main

    Before writing a file, you must define the schema using DBFField objects. You can set the field name, data type (e.g., DBFField.FIELD_TYPE_C for Character, DBFField.FIELD_TYPE_N for Numeric), and field length.

    DBFField field = new DBFField();
    field.setField( "emp_name"); // give a name to the field
    field.setDataType( DBFField.FIELD_TYPE_C); // and set its type
    field.setFieldLength( 25); // and length of the field