Implementing a Simple Java ORM Framework from Scratch

The article walks through building a lightweight Java ORM framework from scratch, showing JDBC drawbacks, defining XML configuration and mapper files, implementing core components like SqlSessionFactory, SimpleExecutor, and reflection‑based parameter and result mapping, and demonstrating a working test that returns POJOs.

Java Tech Enthusiast
Java Tech Enthusiast
Java Tech Enthusiast
Implementing a Simple Java ORM Framework from Scratch

This article walks through the design and implementation of a lightweight ORM framework in Java, inspired by MyBatis. It starts by showing a typical JDBC query and points out its drawbacks such as hard‑coded configuration, inline SQL, and manual result‑set mapping.

Traditional JDBC example:

static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
static final String JDBC_URL = "jdbc:mysql:///user";
static final String USER_NAME = "root";
static final String PASS_WORD = "123456";

public static void main(String[] args) {
    Class.forName(JDBC_DRIVER);
    Connection conn = DriverManager.getConnection(JDBC_URL, USER_NAME, PASS_WORD);
    Statement stmt = conn.createStatement();
    String sql = "SELECT * FROM user";
    ResultSet rs = stmt.executeQuery(sql);
    while(rs.next()){
        int id  = rs.getInt("id");
        int age = rs.getInt("age");
        System.out.println("ID: " + id);
        System.out.println("Age: " + age);
    }
    rs.close();
}

To address these issues the article defines two XML configuration files: a global configuration file that holds data‑source settings and mapper locations, and individual mapper files that declare SQL statements.

<configuration>
    <!-- data source configuration -->
    <dataSource>
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql:///test"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </dataSource>
    <!-- mapper file location -->
    <mapper resource="UserMapper.xml"/>
</configuration>

The UserMapper.xml defines a simple select statement:

<mapper namespace="user">
    <select id="selectList" resultType="com.wwj.pojo.User">
        select * from e_user
    </select>
</mapper>

Key Java components: Resources – loads a file as an InputStream. SqlSessionFactoryBuilder – builds a SqlSessionFactory from the configuration stream. Configuration – stores the DataSource and a Map<String, MapperStatement> of all parsed statements. XmlConfigBuilder – parses the global XML, creates a ComboPooledDataSource, and registers each mapper. XmlMapperBuilder – parses a mapper XML, builds MapperStatement objects and stores them using namespace.id as the key. SimpleExecutor – the core executor that converts MyBatis‑style #{} placeholders to ?, sets parameters via reflection, runs the prepared statement, and maps result rows back to POJOs using PropertyDescriptor.

Excerpt from SimpleExecutor showing SQL preparation and result mapping:

public <T> List<T> query(Configuration configuration, MapperStatement mapperStatement, Object... params) throws Exception {
    Connection connection = configuration.getDataSource().getConnection();
    String sql = mapperStatement.getSql();
    ReplaceSql replaceSql = getReplaceSql(sql);
    PreparedStatement preparedStatement = connection.prepareStatement(replaceSql.getSql());
    // set parameters via reflection
    List<ParameterMapping> parameterMappingList = replaceSql.getParameterMappingList();
    for (int i = 0; i < parameterMappingList.size(); i++) {
        ParameterMapping pm = parameterMappingList.get(i);
        Field field = parameterClass.getDeclaredField(pm.getContent());
        field.setAccessible(true);
        Object value = field.get(params[0]);
        preparedStatement.setObject(i + 1, value);
    }
    ResultSet resultSet = preparedStatement.executeQuery();
    // map result set to POJO
    while (resultSet.next()) {
        Object o = resultClass.newInstance();
        ResultSetMetaData metaData = resultSet.getMetaData();
        for (int i = 1; i <= metaData.getColumnCount(); ++i) {
            String columnName = metaData.getColumnName(i);
            Object columnValue = resultSet.getObject(columnName);
            PropertyDescriptor pd = new PropertyDescriptor(columnName, resultClass);
            pd.getWriteMethod().invoke(o, columnValue);
        }
        list.add(o);
    }
    return (List<T>) list;
}

A simple test demonstrates usage:

@Test
public void test() throws Exception {
    InputStream inputStream = Resources.getResourceAsStream("sqlMapperConfig.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    List<User> userList = sqlSession.selectList("user.selectList");
    System.out.println(userList);
}

The execution prints a list of User objects, confirming that the framework can load configuration, parse mapper files, prepare statements, and map results automatically.

Project structure overview:

com.wwj.config
    - ReplaceSql
    - XmlConfigBuilder
    - XmlMapperBuilder
com.wwj.io
    - Resources
com.wwj.pojo
    - Configuration
    - MapperStatement
com.wwj.sqlSession
    - Executor
    - SqlSession
    - SqlSessionFactory
    - SqlSessionFactoryBuilder
com.wwj.sqlSession.impl
    - DefaultSqlSession
    - DefaultSqlSessionFactory
    - SimpleExecutor
com.wwj.utils
    - GenericTokenParser
    - ParameterMapping
    - ParameterMappingTokenHandler
    - TokenHandler

Overall, the article provides a step‑by‑step guide to building a functional ORM layer, covering configuration parsing, resource loading, session management, SQL preparation, parameter handling, and result mapping.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavadatabaseReflectionMyBatisORMJDBCFramework
Java Tech Enthusiast
Written by

Java Tech Enthusiast

Sharing computer programming language knowledge, focusing on Java fundamentals, data structures, related tools, Spring Cloud, IntelliJ IDEA... Book giveaways, red‑packet rewards and other perks await!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.