Quick Start for a Compile-time Java ORM
Quick Start
Section titled “Quick Start”Lynxus requires Java 21. It generates ordinary Java Mapper implementations during annotation processing and executes them through a small JDBC runtime.
0. Install the Lynxus Skill (recommended)
Section titled “0. Install the Lynxus Skill (recommended)”Lynxus is designed for agent-led development. Install the unified lynxus skill before starting so your agent can scan the project, create a bounded TODO list, configure Lynxus incrementally, and switch into migration mode when it finds an existing MyBatis project. The recommended commands download the small GitHub Release artifact rather than the full Lynxus source tree; each is a single copy-paste line. Pin a versioned Release URL when reproducibility matters.
Codex:
tmp=$(mktemp -d) && curl -fsSL https://github.com/lynxus-project/lynxus/releases/latest/download/lynxus-skill.tar.gz | tar -xz -C "$tmp" && mkdir -p "${CODEX_HOME:-$HOME/.codex}/skills" && cp -R "$tmp/lynxus-skill" "${CODEX_HOME:-$HOME/.codex}/skills/lynxus"Claude Code:
tmp=$(mktemp -d) && curl -fsSL https://github.com/lynxus-project/lynxus/releases/latest/download/lynxus-skill.tar.gz | tar -xz -C "$tmp" && mkdir -p "$HOME/.claude/skills" && cp -R "$tmp/lynxus-skill" "$HOME/.claude/skills/lynxus"Cursor (project-local):
tmp=$(mktemp -d) && curl -fsSL https://github.com/lynxus-project/lynxus/releases/latest/download/lynxus-skill.tar.gz | tar -xz -C "$tmp" && mkdir -p .cursor/skills && cp -R "$tmp/lynxus-skill" .cursor/skills/lynxusIf curl is unavailable, use this wget one-liner for Codex:
tmp=$(mktemp -d) && wget -qO "$tmp/lynxus-skill.tar.gz" https://github.com/lynxus-project/lynxus/releases/latest/download/lynxus-skill.tar.gz && tar -xzf "$tmp/lynxus-skill.tar.gz" -C "$tmp" && mkdir -p "${CODEX_HOME:-$HOME/.codex}/skills" && cp -R "$tmp/lynxus-skill" "${CODEX_HOME:-$HOME/.codex}/skills/lynxus"From a local checkout, the equivalent one-liner is:
tmp=$(mktemp -d) && git clone --depth 1 https://github.com/lynxus-project/lynxus.git "$tmp/lynxus" >/dev/null && mkdir -p "${CODEX_HOME:-$HOME/.codex}/skills" && cp -R "$tmp/lynxus/skills/lynxus" "${CODEX_HOME:-$HOME/.codex}/skills/lynxus"Then ask your agent to use $lynxus. The same skill covers new integrations and MyBatis migration; it scans first, writes a reviewable TODO list, and pauses when a decision needs you.
1. Add the Dependency
Section titled “1. Add the Dependency”For Spring Boot:
<dependency> <groupId>io.github.lynxus</groupId> <artifactId>lynxus-spring-boot-starter</artifactId> <version>1.0.0-SNAPSHOT</version></dependency>For standalone JDBC, depend on lynxus-core and add lynxus-processor to the compiler’s
annotation processor path. While working from this repository, install snapshots locally first:
mvn -DskipTests installExplicitly enable the processor with Maven:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <proc>full</proc> <annotationProcessorPaths> <path> <groupId>io.github.lynxus</groupId> <artifactId>lynxus-processor</artifactId> <version>1.0.0-SNAPSHOT</version> </path> </annotationProcessorPaths> <annotationProcessors> <annotationProcessor>io.github.lynxus.compile.LynxusProcessor</annotationProcessor> </annotationProcessors> </configuration></plugin>2. Define a Mapper
Section titled “2. Define a Mapper”package com.example.user.mapper;
public record User(Long id, String name) {}package com.example.user.mapper;
import io.github.lynxus.annotation.Insert;import io.github.lynxus.annotation.Mapper;import io.github.lynxus.annotation.Param;import io.github.lynxus.annotation.Select;
import java.util.List;
@Mapperpublic interface UserMapper {
@Insert("INSERT INTO users (id, name) VALUES (#{id}, #{name})") int insert(@Param("id") Long id, @Param("name") String name);
@Select("SELECT id, name FROM users WHERE id = #{id}") User findById(@Param("id") Long id);
@Select({ "<script>", "SELECT id, name FROM users WHERE 1=1", "<if test='name != null'>AND name LIKE #{name}</if>", "</script>" }) List<User> findByName(@Param("name") String name);}Compilation generates UserMapperImpl under target/generated-sources/annotations. The implementation directly implements UserMapper and receives one SqlExecutor through its constructor. Inspect that directory after you compile. The excerpts below follow the current processor shape; they are documentation copy, not checked-in generated files.
Static SQL becomes a QueryDefinition and an ordinary method that calls SqlExecutor:
private static final QueryDefinition<User> FIND_BY_ID_DEFINITION = QueryDefinition.assembled( "com.example.user.mapper.UserMapper.findById", "SELECT id, name FROM users WHERE id = ?", ExecutionPlan.SqlSource.ANNOTATION, row -> new User((Long) row.get(0), (String) row.get(1)), /* parameter binders, statement options, type routing */);
@Overridepublic User findById(Long id) { QueryExecutionPlan<User> executionPlan = buildFindByIdExecutionPlan(id); QueryResult<User> executionResult = sqlExecutor.query(executionPlan); return executionResult.oneOrNull();}
private QueryExecutionPlan<User> buildFindByIdExecutionPlan(Long id) { return FIND_BY_ID_DEFINITION.bind(id);}Supported dynamic SQL becomes Java control flow, not OGNL evaluation at invoke time:
BoundSqlBuilder sql = BoundSqlBuilder.create("com.example.user.mapper.UserMapper#findByName");sql.append("SELECT id, name FROM users WHERE 1=1");if (name != null) { sql.append("AND name LIKE ?"); sql.parameter(name, null, java.lang.String.class, java.sql.JDBCType.VARCHAR);}return FIND_BY_NAME_DEFINITION.bind(sql.build());3. Assemble the Mapper
Section titled “3. Assemble the Mapper”For Spring Boot, bind the Mapper package to a named DataSource:
lynxus: mapper-bindings: - package-name: com.example.user.mapper data-source: dataSourceFor standalone use, create a JDBC assembly and construct the generated implementation directly.
Next Steps
Section titled “Next Steps”- Read the architecture overview.
- Choose a value or row mapping strategy.
- Configure the Spring Boot integration.
- Run the basic Mapper example.