Zum Hauptinhalt springen
tsecurity.de LIVE
Echtzeit-Radar & Feeds
Alle RSS Feeds
👥 Community & Social
Sichere ProgrammierungLarge AI Labs Face Regulatory Capture Allegations(21.09.2026 um 05:18 Uhr)
Sichere ProgrammierungWhat people are building with Jev: a look through nine awesome lists(21.09.2026 um 05:44 Uhr)
IT Security Toolsnetwatch v0.32.3(21.09.2026 um 04:36 Uhr)
Sichere ProgrammierungLarge AI Labs Face Regulatory Capture Allegations(21.09.2026 um 05:18 Uhr)
Sichere ProgrammierungWhat people are building with Jev: a look through nine awesome lists(21.09.2026 um 05:44 Uhr)
IT Security Toolsnetwatch v0.32.3(21.09.2026 um 04:36 Uhr)
Intelligence View
⚡ tsecurity.de Intelligence

easy-query: A Modern, Type-Safe ORM for Java That Actually Makes Sense

Reagiere als Erste:r — dein Feedback zählt!

GitHub: https://github.com/dromara/easy-query

Documentation: https://www.easy-query.com/easy-query-doc/en/

Why Another Java ORM?

Let me show you a problem first. Here's how you query data with traditional Java ORMs:

// JPA Criteria API - Runtime strings, no type safety
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<User> cq = cb.createQuery(User.class);
Root<User> user = cq.from(User.class);
cq.where(cb.and(
    cb.equal(user.get("name"), "John"),  // String reference!
    cb.greaterThan(user.get("age"), 18)  // Typo? Find out at runtime!
));

What if I told you it could look like this instead?

// easy-query - Compile-time safety, fluent API
List<User> users = easyEntityQuery.queryable(User.class)
    .where(user -> {
        user.name().eq("John");   // Full IntelliSense support
        user.age().gt(18);        // Typo? Compiler catches it!
    })
    .toList();

That's what easy-query brings to Java - a modern, type-safe ORM that feels natural to write and is safe to refactor.

Core Philosophy

easy-query is built on three principles:

  1. Type Safety First - If it compiles, it works. No runtime surprises.
  2. Developer Experience - IntelliSense everywhere, readable code, easy refactoring.
  3. Performance - Smart query optimization, N+1 prevention, efficient SQL generation.

Key Features

1. 🔒 Compile-Time Type Safety

No more string-based field references:

// ❌ Old way - Runtime errors waiting to happen
query.where("userNam = ?", name);  // Typo in field name!

// ✅ easy-query - Compiler catches typos
List<User> users = easyEntityQuery.queryable(User.class)
    .where(user -> user.userName().eq(name))  // IDE autocomplete!
    .toList();

2. 🎯 Fluent Query API

Write queries that read like natural language:

// Complex query that's still readable
List<Order> orders = easyEntityQuery.queryable(Order.class)
    .where(order -> {
        order.status().eq("ACTIVE");
        order.amount().between(100, 1000);
        order.createTime().ge(LocalDateTime.now().minusDays(30));
    })
    .orderBy(order -> order.createTime().desc())
    .limit(10)
    .toList();

3. 🔗 Smart Navigation Properties

Load related data without N+1 queries:

// Load user with roles and company in efficient batched queries
List<User> users = easyEntityQuery.queryable(User.class)
    .include(user -> user.roles())      // Batched query #1
    .include(user -> user.company())    // Batched query #2
    .toList();

// Executed SQL:
// Query 1: SELECT * FROM user
// Query 2: SELECT * FROM user_role WHERE user_id IN (1,2,3,...)
// Query 3: SELECT * FROM role WHERE id IN (10,11,12,...)
// Query 4: SELECT * FROM company WHERE id IN (100,101,...)

No cartesian products, no manual optimization needed!

4. 📊 Type-Safe Projections

Map to DTOs with full type safety:

@Data
public class UserSummaryDTO {
    private String userId;
    private String fullName;
    private String companyName;
    private int roleCount;
}

// Type-safe mapping
List<UserSummaryDTO> summaries = easyEntityQuery.queryable(User.class)
    .include(user -> user.roles())
    .include(user -> user.company())
    .select(user -> new UserSummaryDTOProxy()
        .userId().set(user.id())
        .fullName().set(user.firstName().concat(" ").concat(user.lastName()))
        .companyName().set(user.company().name())
        .roleCount().set(user.roles().count())
    )
    .toList();

5. 📈 Powerful GROUP BY

Aggregation queries with strong typing:

// Monthly sales statistics
List<SalesStatDTO> stats = easyEntityQuery.queryable(Order.class)
    .where(order -> order.status().eq("COMPLETED"))
    .groupBy(order -> GroupKeys.of(
        order.userId(),
        order.createTime().format("yyyy-MM")
    ))
    .select(SalesStatDTO.class, group -> Select.of(
        group.key1().as(SalesStatDTO::getUserId),
        group.key2().as(SalesStatDTO::getMonth),
        group.count().as(SalesStatDTO::getOrderCount),
        group.sum(s -> s.amount()).as(SalesStatDTO::getTotalAmount),
        group.avg(s -> s.amount()).as(SalesStatDTO::getAvgAmount),
        group.max(s -> s.amount()).as(SalesStatDTO::getMaxAmount)
    ))
    .having(group -> group.count().gt(5L))
    .orderBy(SalesStatDTO::getTotalAmount, false)
    .toList();

// Generated SQL:
// SELECT 
//     user_id,
//     DATE_FORMAT(create_time, '%Y-%m') as month,
//     COUNT(*) as order_count,
//     SUM(amount) as total_amount,
//     AVG(amount) as avg_amount,
//     MAX(amount) as max_amount
// FROM t_order
// WHERE status = 'COMPLETED'
// GROUP BY user_id, DATE_FORMAT(create_time, '%Y-%m')
// HAVING COUNT(*) > 5
// ORDER BY total_amount DESC

6. 🔄 Change Tracking

Track entity modifications automatically:

try (TrackContext track = easyQueryClient.startTrack()) {
    User user = easyEntityQuery.queryable(User.class)
        .whereById("1")
        .firstOrNull();

    user.setName("John Doe");
    user.setEmail("[email protected]");

    track.saveChanges();  // Only updates changed fields!
}

// Generated SQL:
// UPDATE t_user SET name = ?, email = ? WHERE id = ?
// Only the modified fields are in the UPDATE statement

7. ⚡ Bulk Operations

Efficient batch updates and deletes:

// Bulk update
long updated = easyEntityQuery.updatable(User.class)
    .set(user -> {
        user.status().set("INACTIVE");
        user.updateTime().set(LocalDateTime.now());
    })
    .where(user -> user.lastLoginTime().lt(LocalDateTime.now().minusDays(90)))
    .executeRows();

// Bulk delete
long deleted = easyEntityQuery.deletable(Order.class)
    .where(order -> {
        order.status().eq("CANCELLED");
        order.createTime().lt(LocalDateTime.now().minusYears(2));
    })
    .executeRows();

8. 🎨 Complex Joins

Multi-table queries with type safety:

// Three-table join with conditions
List<OrderDetailDTO> details = easyEntityQuery.queryable(Order.class)
    .leftJoin(User.class, (order, user) -> 
        order.userId().eq(user.id())
    )
    .leftJoin(Product.class, (order, user, product) -> 
        order.productId().eq(product.id())
    )
    .where((order, user, product) -> {
        order.status().eq("COMPLETED");
        user.vipLevel().gt(3);
        product.category().eq("Electronics");
    })
    .select((order, user, product) -> new OrderDetailDTOProxy()
        .orderId().set(order.id())
        .userName().set(user.name())
        .productName().set(product.name())
        .totalPrice().set(order.quantity().multiply(product.price()))
    )
    .toList();

Code Generation Magic

easy-query uses annotation processors to generate type-safe proxies at compile time:

// Step 1: Annotate your entity
@Table("t_user")
@EntityProxy  // ← This is all you need!
@Data
public class User {
    @Column(primaryKey = true)
    private String id;
    private String name;
    private String email;
    private Integer age;

    @Navigate(value = RelationTypeEnum.ManyToMany, ...)
    private List<Role> roles;
}

// Step 2: Proxy is auto-generated
// You get UserProxy with type-safe methods:
// - user.id() → SQLStringTypeColumn<UserProxy>
// - user.name() → SQLStringTypeColumn<UserProxy>
// - user.age() → SQLIntTypeColumn<UserProxy>
// - user.roles() → Navigation methods

// Step 3: Use it!
List<User> adults = easyEntityQuery.queryable(User.class)
    .where(user -> user.age().ge(18))  // Full type safety!
    .toList();

Zero runtime overhead, full compile-time safety!

Advanced Features

Subqueries

// Find users who have more than 5 completed orders
List<User> activeUsers = easyEntityQuery.queryable(User.class)
    .where(user -> {
        user.id().in(
            easyEntityQuery.queryable(Order.class)
                .where(order -> order.status().eq("COMPLETED"))
                .groupBy(order -> GroupKeys.of(order.userId()))
                .having(group -> group.count().gt(5L))
                .select(order -> order.userId())
        );
    })
    .toList();

Conditional Queries

// Build queries dynamically
public List<User> searchUsers(String name, Integer minAge, String city) {
    return easyEntityQuery.queryable(User.class)
        .where(user -> {
            user.name().like(name != null, name);  // Only if name provided
            user.age().ge(minAge != null, minAge); // Only if minAge provided
            user.city().eq(city != null, city);    // Only if city provided
        })
        .toList();
}

Pagination

// Built-in pagination support
EasyPageResult<User> page = easyEntityQuery.queryable(User.class)
    .where(user -> user.status().eq("ACTIVE"))
    .orderBy(user -> user.createTime().desc())
    .toPageResult(1, 20);  // page 1, 20 items per page

System.out.println("Total: " + page.getTotal());
System.out.println("Data: " + page.getData());

Table Sharding Support

// Built-in sharding for high-traffic apps!
@Table(value = "t_order", shardingInitializer = MonthTableShardingInitializer.class)
@EntityProxy
public class Order {
    @Column(primaryKey = true)
    private String id;

    @ShardingTableKey  // Sharding key
    private LocalDateTime createTime;

    private BigDecimal amount;
}

// Query automatically routes to correct sharded tables
List<Order> orders = easyEntityQuery.queryable(Order.class)
    .where(order -> 
        order.createTime().between(
            LocalDateTime.of(2024, 1, 1, 0, 0),
            LocalDateTime.of(2024, 3, 31, 23, 59)
        )
    )
    .toList();

// Executes across multiple tables in parallel:
// t_order_202401, t_order_202402, t_order_202403

Multi-Database Support

Works with all major databases out of the box:

  • ✅ MySQL / MariaDB
  • ✅ PostgreSQL
  • ✅ SQL Server
  • ✅ Oracle
  • ✅ SQLite
  • ✅ H2
  • ✅ DuckDB
  • ✅ DM (达梦), KingBase, GaussDB
// Easy to switch databases
EasyQueryClient easyQueryClient = EasyQueryBootstrapper
    .defaultBuilderConfiguration()
    .setDefaultDataSource(dataSource)
    .optionConfigure(op -> {
        op.setDatabase(DatabaseType.MYSQL);  // Just change this!
    })
    .build();

Performance Comparison

Feature easy-query JPA/Hibernate MyBatis
Type Safety ✅ Full ⚠️ Partial ❌ None
N+1 Prevention ✅ Built-in ⚠️ Manual ⚠️ Manual
Refactoring ✅ Safe ⚠️ Risky ❌ Dangerous
Learning Curve ✅ Easy ⚠️ Steep ✅ Easy
Performance ✅ Fast ✅ Good ✅ Fast
SQL Control ✅ Full ⚠️ Limited ✅ Full
Code Generation ✅ Compile-time ⚠️ Runtime ❌ None

Getting Started

1. Add Dependencies

<dependency>
    <groupId>com.easy-query</groupId>
    <artifactId>sql-springboot-starter</artifactId>
    <version>3.1.49</version>
</dependency>

<!-- Annotation processor for code generation -->
<dependency>
    <groupId>com.easy-query</groupId>
    <artifactId>sql-processor</artifactId>
    <version>3.1.49</version>
    <scope>provided</scope>
</dependency>

2. Configure

# application.yml
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb
    username: root
    password: password

easy-query:
  enable: true
  database: mysql
  print-sql: true
  name-conversion: underlined  # camelCase → snake_case

3. Create Entity

@Table("t_user")
@EntityProxy  // Enable code generation
@Data
public class User {
    @Column(primaryKey = true)
    private String id;
    private String name;
    private String email;
    private Integer age;
    private LocalDateTime createTime;
}

4. Query!

@Service
public class UserService {
    @Resource
    private EasyEntityQuery easyEntityQuery;

    public List<User> getActiveUsers() {
        return easyEntityQuery.queryable(User.class)
            .where(user -> {
                user.status().eq("ACTIVE");
                user.age().ge(18);
            })
            .orderBy(user -> user.createTime().desc())
            .toList();
    }
}

Real-World Example

Here's a complete service class showing various features:

@Service
@RequiredArgsConstructor
public class OrderService {
    private final EasyEntityQuery easyEntityQuery;

    // Complex query with joins and aggregation
    public List<OrderStatDTO> getMonthlyStats(String userId, int year) {
        return easyEntityQuery.queryable(Order.class)
            .where(order -> {
                order.userId().eq(userId);
                order.createTime().format("yyyy").eq(String.valueOf(year));
                order.status().eq("COMPLETED");
            })
            .groupBy(order -> GroupKeys.of(
                order.createTime().format("yyyy-MM")
            ))
            .select(OrderStatDTO.class, group -> Select.of(
                group.key1().as(OrderStatDTO::getMonth),
                group.count().as(OrderStatDTO::getOrderCount),
                group.sum(s -> s.totalAmount()).as(OrderStatDTO::getTotalSales)
            ))
            .orderBy(OrderStatDTO::getMonth, true)
            .toList();
    }

    // Query with navigation properties
    public OrderDetailVO getOrderDetail(String orderId) {
        Order order = easyEntityQuery.queryable(Order.class)
            .whereById(orderId)
            .include(o -> o.orderItems())  // Load items
            .include(o -> o.user())        // Load user
            .include(o -> o.shipping())    // Load shipping
            .firstOrNull();

        if (order == null) {
            throw new OrderNotFoundException(orderId);
        }

        return OrderDetailVO.from(order);
    }

    // Pagination
    public EasyPageResult<OrderListDTO> searchOrders(
        OrderSearchRequest request, int page, int size
    ) {
        return easyEntityQuery.queryable(Order.class)
            .where(order -> {
                order.userId().eq(request.getUserId() != null, request.getUserId());
                order.status().eq(request.getStatus() != null, request.getStatus());
                order.createTime().ge(request.getStartDate() != null, request.getStartDate());
                order.createTime().le(request.getEndDate() != null, request.getEndDate());
            })
            .orderBy(order -> order.createTime().desc())
            .select(OrderListDTO.class)
            .toPageResult(page, size);
    }

    // Bulk update
    @Transactional
    public long cancelExpiredOrders() {
        return easyEntityQuery.updatable(Order.class)
            .set(order -> {
                order.status().set("CANCELLED");
                order.cancelTime().set(LocalDateTime.now());
            })
            .where(order -> {
                order.status().eq("PENDING");
                order.createTime().lt(LocalDateTime.now().minusHours(24));
            })
            .executeRows();
    }
}

Why Choose easy-query?

For Developers

  • Write less boilerplate, more business logic
  • Catch errors at compile time, not production
  • Refactor with confidence
  • IntelliSense makes you productive

For Teams

  • Easy to review - code is self-documenting
  • New members get productive quickly
  • Less debugging, more feature building
  • Consistent coding style

For Applications

  • Optimized SQL generation
  • No N+1 query surprises
  • Efficient batch operations
  • Built-in sharding for scale

Community & Resources

Star the repo if you find it useful! ⭐

Try It Now

The best way to understand easy-query is to try it. Here's a minimal example you can run:

// 1. Add dependencies (see above)
// 2. Configure database (see above)
// 3. Create your first entity:

@Table("t_blog")
@EntityProxy
@Data
public class Blog {
    @Column(primaryKey = true)
    private String id;
    private String title;
    private String content;
    private Integer views;
    private LocalDateTime publishTime;
}

// 4. Start querying:

@Service
public class BlogService {
    @Resource
    private EasyEntityQuery easyEntityQuery;

    public List<Blog> getPopularBlogs() {
        return easyEntityQuery.queryable(Blog.class)
            .where(blog -> blog.views().gt(1000))
            .orderBy(blog -> blog.publishTime().desc())
            .limit(10)
            .toList();
    }
}

Final Thoughts

easy-query brings modern, type-safe database access to Java. It's:

  • Simple - Easy to learn, easy to use
  • Safe - Compile-time type checking everywhere
  • Powerful - All features you need for complex applications
  • Fast - Optimized SQL generation and execution

If you're tired of string-based queries and runtime errors, give easy-query a try. Your future self will thank you! 🚀

What do you think? Have you tried easy-query? What's your biggest pain point with current Java ORMs? Let's discuss in the comments! 💬

Found this helpful? Give it a ⭐ on GitHub!

Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten easy-query: A Modern, Type-Safe ORM for Java That Actually Makes Sense

Thematisch verwandte Begriffe: easyquery, Modern, TypeSafe, Java · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-93977 | A vulnerability was determined in code-projects Assessment Management 1.…
Advisory →
TTS Reader • tsecurity.de Voice
tsecurity.de Icon
tsecurity.de App
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag
Themen-Radar & Intelligence Matrix
Echtzeit-Taxonomie nach Angriffsvektoren & Plattformen

tsecurity.de Live Threat Radar

🔴 LIVE RADAR
MONITORING
AKTIV
CVE-DATENBANK
LIVE
🔍
Community Radar & Live Chat
Sentinel Bot online • Live-Stream
Dein Cluster: Security Explorer
Match:
lädt…
Verbindung zum Community-Stream wird aufgebaut...
Bearbeitungsmodus — Senden überschreibt deine Nachricht
Community-Puls — was gerade passiert
lädt…
Aktivitäten deiner Analysten
lädt…
Neues Thema oder Eilmeldung einreichen

Reiche interessante Links, Zero-Days oder Debatten ein. Die Community entscheidet per Upvote über die Veröffentlichung.

Heiß diskutierte Einreichungen
🔖 Gespeicherte Artikel
📂 Keine gespeicherten Artikel vorhanden.
Zurück Ziehen Vor
Links: vorheriger Artikel Rechts: nächster Artikel unten: schließen
News NIS-2 Frühwarnung Tier-1 Intel ⏱️ 3 Min vor 10 Min
Artikeldaten werden geladen...

Zurück: vorheriger Vor: nächster
↗ Original-Quelle
Social Reaktionen Deine Reaktion zählt
Einstufung & Relevanz-Poll 0 Stimmen
In sozialen Netzwerken teilen 1-Klick