Get your database integration up and running in minutes.
Simple SQL Generator is a Java annotation processor that automatically generates type-safe SQL execution code at compile time. Write clean, maintainable data access code without runtime reflection or complex ORM overhead.
- β‘ Rapid Prototyping - Get database queries working in minutes with just annotations, perfect for MVPs and proof-of-concepts
- π Zero Runtime Cost - All SQL generated at compile time, no reflection overhead
- π‘οΈ Type Safe - Compilation errors catch SQL parameter mismatches early
- ποΈ Multi-Database Support - MySQL, PostgreSQL, Oracle, H2, and more
- β JDK 11-21+ Compatible - Wide compatibility range for modern and legacy projects
- π¦ Minimal Dependencies - Only requires Google Auto Service for annotation processing
- π§ IDE Friendly - Full IntelliJ IDEA and Eclipse support with code completion
Your project must include Spring JDBC dependencies:
Gradle:
dependencies {
implementation 'org.springframework:spring-jdbc:6.0.0+'
implementation 'org.springframework:spring-tx:6.0.0+'
// Or use Spring Boot starter
// implementation 'org.springframework.boot:spring-boot-starter-jdbc'
// Database driver (choose one)
runtimeOnly 'com.h2database:h2' // H2
runtimeOnly 'org.postgresql:postgresql' // PostgreSQL
runtimeOnly 'com.mysql:mysql-connector-j' // MySQL
}Maven:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>6.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>6.0.0</version>
</dependency>
<!-- Database driver (choose one) -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>Add Simple SQL Generator to your build tool:
Maven:
<dependency>
<groupId>io.github.simplesqlgen</groupId>
<artifactId>simple-sql-generator</artifactId>
<version>1.0.2</version>
<scope>provided</scope>
</dependency>Gradle:
compileOnly 'io.github.simplesqlgen:simple-sql-generator:1.0.2'
annotationProcessor 'io.github.simplesqlgen:simple-sql-generator:1.0.2'1. Define your entity:
public class User {
private Long id;
private String name;
private String email;
private boolean active;
// constructors, getters, setters...
}2. Create a repository class:
@Component
@SqlGenerator(entity = User.class, tableName = "users")
public class UserRepository {
// These fields will be automatically injected by the processor
// @Autowired private JdbcTemplate jdbcTemplate;
// @Autowired private NamedParameterJdbcTemplate namedJdbcTemplate;
// Native SQL queries
@NativeQuery("SELECT * FROM users WHERE id = ?")
public User findById(Long id) {
// Implementation will be generated at compile time
return null;
}
@NativeQuery("UPDATE users SET active = ? WHERE id = ?")
public int updateActiveStatus(boolean active, Long id) {
// Implementation will be generated at compile time
return 0;
}
// Auto-generated queries from method names
public List<User> findByName(String name) {
// Implementation will be generated at compile time
return null;
}
public List<User> findByEmailAndActive(String email, boolean active) {
// Implementation will be generated at compile time
return null;
}
public User findByEmail(String email) {
// Implementation will be generated at compile time
return null;
}
// CRUD operations
public User save(User user) {
// Implementation will be generated at compile time
return null;
}
public int deleteById(Long id) {
// Implementation will be generated at compile time
return 0;
}
public boolean existsByEmail(String email) {
// Implementation will be generated at compile time
return false;
}
public long countByActive(boolean active) {
// Implementation will be generated at compile time
return 0;
}
}3. Use in your application:
@Component
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User createUser(String name, String email) {
User user = new User(name, email, true);
return userRepository.save(user);
}
public List<User> findActiveUsersByName(String name) {
return userRepository.findByNameAndActive(name, true);
}
}Write raw SQL with type-safe parameter binding:
@NativeQuery(value = "SELECT u.*, p.title as profile_title FROM users u LEFT JOIN profiles p ON u.id = p.user_id WHERE u.created_at > ?",
resultType = UserProfile.class)
public List<UserProfile> findUsersWithProfilesAfter(LocalDateTime date) {
// Implementation generated at compile time
return null;
}
// Named parameters
@NativeQuery("SELECT * FROM users WHERE name = :name AND age > :minAge")
public List<User> findByNameAndAgeGreaterThan(@Param("name") String name, @Param("minAge") int minAge) {
// Implementation generated at compile time
return null;
}Method names are parsed to generate SQL automatically:
// Generates: SELECT * FROM users WHERE name = ?
public List<User> findByName(String name) {
return null; // Implementation generated at compile time
}
// Generates: SELECT * FROM users WHERE email = ? AND active = ?
public List<User> findByEmailAndActive(String email, boolean active) {
return null; // Implementation generated at compile time
}
// Generates: SELECT COUNT(*) FROM users WHERE active = ?
public long countByActive(boolean active) {
return 0; // Implementation generated at compile time
}
// Generates: DELETE FROM users WHERE id = ?
public int deleteById(Long id) {
return 0; // Implementation generated at compile time
}
// Generates: SELECT COUNT(*) FROM users WHERE email = ?
public boolean existsByEmail(String email) {
return false; // Implementation generated at compile time
}// LIKE operations
// Generates: SELECT * FROM users WHERE name LIKE ?
public List<User> findByNameContaining(String namePart) {
return null; // Implementation generated at compile time
}
// Generates: SELECT * FROM users WHERE UPPER(email) LIKE UPPER(?)
public List<User> findByEmailStartingWithIgnoreCase(String emailPrefix) {
return null; // Implementation generated at compile time
}
// Boolean literals
// Generates: SELECT * FROM users WHERE active = ?
public List<User> findByActiveTrue() {
return null; // Implementation generated at compile time
}
// OR operations
// Generates: SELECT * FROM users WHERE name = ? OR email = ?
public List<User> findByNameOrEmail(String name, String email) {
return null; // Implementation generated at compile time
}
// Aggregation functions
// Generates: SELECT SUM(age) FROM users WHERE active = ?
public Integer sumAgeByActiveTrue() {
return 0; // Implementation generated at compile time
}
// Complex combinations
// Generates: SELECT DISTINCT email FROM users WHERE UPPER(name) LIKE UPPER(?) AND age BETWEEN ? AND ? ORDER BY email ASC
public List<String> findDistinctEmailByNameContainingIgnoreCaseAndAgeBetweenOrderByEmailAsc(String namePart, int minAge, int maxAge) {
return null; // Implementation generated at compile time
}Flexible result mapping strategies:
@NativeQuery(value = "SELECT name, email FROM users",
mappingType = ResultMappingType.CONSTRUCTOR,
resultType = UserSummary.class)
public List<UserSummary> getUserSummaries() {
return null; // Implementation generated at compile time
}
@NativeQuery(value = "SELECT id, name, email, created_at FROM users",
mappingType = ResultMappingType.FIELD_MAPPING,
columnMapping = {"id", "name", "email", "createdAt"})
public List<User> getAllUsers() {
return null; // Implementation generated at compile time
}Simple SQL Generator supports multiple databases with standard SQL generation:
Supported Databases:
- MySQL 5.7+
- PostgreSQL 10+
- Oracle 12c+
- H2 Database
- SQL Server 2017+
Control how entity names map to table/column names:
@Component
@SqlGenerator(entity = User.class,
namingStrategy = NamingStrategy.SNAKE_CASE) // user_profile -> USER_PROFILE
public class UserRepository { }
@Component
@SqlGenerator(entity = User.class,
namingStrategy = NamingStrategy.CAMEL_CASE) // userProfile -> userProfile
public class UserRepository { }Use only @NativeQuery annotations without entity-based generation:
@Component
@SqlGenerator(nativeQueryOnly = true)
public class CustomQueryRepository {
@NativeQuery("SELECT custom_function(?) as result")
public String executeCustomFunction(String input) {
return null; // Implementation generated at compile time
}
}Simple SQL Generator integrates seamlessly with Spring Boot:
@Repository
@SqlGenerator(entity = User.class)
public class UserRepository {
// Repository methods with empty bodies that will be filled at compile time
public List<User> findAll() {
return null; // Implementation generated
}
}
@Service
public class UserService {
@Autowired
private UserRepository userRepository; // Auto-injected
}Write clean tests for your repositories:
@Test
void testUserRepository() {
User user = new User("John Doe", "john@example.com", true);
User saved = userRepository.save(user);
assertThat(saved.getId()).isNotNull();
assertThat(userRepository.existsByEmail("john@example.com")).isTrue();
}| Feature | Simple SQL Generator | MyBatis | JPA/Hibernate | JOOQ |
|---|---|---|---|---|
| Compile-time generation | β | β | β | β |
| Zero runtime overhead | β | β | β | β |
| Type safety | β | β | β | |
| Native SQL support | β | β | β | |
| Learning curve | Low | Medium | High | High |
| Annotation-based | β | β | β |
After building your project, you can inspect the generated SQL queries to ensure they match your expectations.
# Gradle
./gradlew build
# Maven
mvn compilebuild/classes/java/main/your/package/YourRepository.class
Open the generated .class file in your IDE to see the decompiled implementation:
@Component
public class UserRepository {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
public List<User> findByName(String name) {
return this.jdbcTemplate.query(
"SELECT * FROM users WHERE name = ?",
new BeanPropertyRowMapper(User.class),
name
);
}
public List<User> findByAgeGreaterThan(int age) {
return this.jdbcTemplate.query(
"SELECT * FROM users WHERE age > ?",
new BeanPropertyRowMapper(User.class),
age
);
}
public long countByActive(boolean active) {
return (Long) this.jdbcTemplate.queryForObject(
"SELECT COUNT(*) FROM users WHERE active = ?",
Long.class,
active
);
}
}- β SQL Syntax: Are the generated queries syntactically correct?
- β
Parameter Binding: Are parameters properly bound with
?placeholders? - β Return Types: Do return types match your method signatures?
- β
Field Injection: Are
@Autowiredfields properly injected? - β Naming Convention: Are column names correctly mapped (snake_case β camelCase)?
- Query Issues: Check the generated SQL against your database schema
- Type Mismatches: Verify return types match your entity fields
- Missing Fields: Ensure entity fields are accessible (have getters/setters)
- Naming Problems: Check your
@SqlGeneratortableName and field mappings
- JDK: 11, 17, 21+ (tested and verified)
- Build Tools: Maven 3.6+, Gradle 6.0+
- Databases: MySQL, PostgreSQL, Oracle, H2, SQL Server
- IDE: IntelliJ IDEA, Eclipse, VS Code
We welcome contributions! Please see our Contributing Guide for details.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
- Inspired by the simplicity of Lombok and the power of JOOQ
- Built on top of Google's Auto Service annotation processor framework
- Thanks to the Java community for annotation processing standards (JSR 269)
Made with β€οΈ by the Simple SQL Generator team
Star β this repo if you find it useful!