Skip to content
WeftKitBeta

Java & Spring Boot Integration

This guide covers connecting to every WeftKit engine from Java. All examples use standard, widely-available drivers and frameworks — no WeftKit-specific SDK is required. Point your existing driver at weftkit-standalone and your existing application code works as-is.

Prerequisites

Add the following dependencies to your project. Pick the sections relevant to the engines you are using.

Maven (pom.xml)

xml
<!-- WeftKitRel — PostgreSQL JDBC -->
<dependency>
  <groupId>org.postgresql</groupId>
  <artifactId>postgresql</artifactId>
  <version>42.7.3</version>
</dependency>

<!-- WeftKitRel — Spring Boot + Spring Data JDBC -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>

<!-- WeftKitRel — Hibernate / JPA -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<!-- WeftKitDoc — MongoDB Java Driver (sync) -->
<dependency>
  <groupId>org.mongodb</groupId>
  <artifactId>mongodb-driver-sync</artifactId>
  <version>5.1.0</version>
</dependency>

<!-- WeftKitDoc — Spring Data MongoDB -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

<!-- WeftKitMem — Jedis -->
<dependency>
  <groupId>redis.clients</groupId>
  <artifactId>jedis</artifactId>
  <version>5.1.2</version>
</dependency>

<!-- WeftKitMem — Lettuce (async) -->
<dependency>
  <groupId>io.lettuce</groupId>
  <artifactId>lettuce-core</artifactId>
  <version>6.3.2.RELEASE</version>
</dependency>

<!-- WeftKitMem — Spring Data Redis -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!-- WeftKitGraph — Neo4j Java Driver -->
<dependency>
  <groupId>org.neo4j.driver</groupId>
  <artifactId>neo4j-java-driver</artifactId>
  <version>5.21.0</version>
</dependency>

<!-- WeftKitKV — AWS SDK for Java v2 (DynamoDB) -->
<dependency>
  <groupId>software.amazon.awssdk</groupId>
  <artifactId>dynamodb</artifactId>
  <version>2.25.60</version>
</dependency>

Gradle (build.gradle)

groovy
dependencies {
    // WeftKitRel — PostgreSQL JDBC
    implementation 'org.postgresql:postgresql:42.7.3'

    // WeftKitRel — Spring Boot Data JDBC
    implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'

    // WeftKitRel — Hibernate / JPA
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

    // WeftKitDoc — MongoDB Java Driver
    implementation 'org.mongodb:mongodb-driver-sync:5.1.0'

    // WeftKitDoc — Spring Data MongoDB
    implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'

    // WeftKitMem — Jedis
    implementation 'redis.clients:jedis:5.1.2'

    // WeftKitMem — Lettuce
    implementation 'io.lettuce:lettuce-core:6.3.2.RELEASE'

    // WeftKitMem — Spring Data Redis
    implementation 'org.springframework.boot:spring-boot-starter-data-redis'

    // WeftKitGraph — Neo4j Java Driver
    implementation 'org.neo4j.driver:neo4j-java-driver:5.21.0'

    // WeftKitKV — AWS SDK v2 DynamoDB
    implementation 'software.amazon.awssdk:dynamodb:2.25.60'
}

WeftKitRel — PostgreSQL via JDBC

WeftKitRel speaks the PostgreSQL v3 wire protocol, so the standard PostgreSQL JDBC driver connects without any modification.

Default port: 5432

Basic Connection and Query

java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class RelExample {

    private static final String URL  = "jdbc:postgresql://localhost:5432/mydb";
    private static final String USER = "app_user";
    private static final String PASS = "secret";

    public static void main(String[] args) throws ClassNotFoundException {
        // Load driver explicitly (optional with JDBC 4+)
        Class.forName("org.postgresql.Driver");

        // Try-with-resources closes the connection automatically
        try (Connection conn = DriverManager.getConnection(URL, USER, PASS)) {

            // --- INSERT with PreparedStatement (always use ? placeholders) ---
            String insertSql = "INSERT INTO products (name, price, stock) VALUES (?, ?, ?)";
            try (PreparedStatement ps = conn.prepareStatement(insertSql)) {
                ps.setString(1, "Widget Pro");
                ps.setDouble(2, 29.99);
                ps.setInt(3, 250);
                int rowsInserted = ps.executeUpdate();
                System.out.println("Inserted: " + rowsInserted + " row(s)");
            }

            // --- SELECT with ResultSet iteration ---
            String selectSql = "SELECT id, name, price FROM products WHERE price < ? ORDER BY price";
            try (PreparedStatement ps = conn.prepareStatement(selectSql)) {
                ps.setDouble(1, 50.00);
                try (ResultSet rs = ps.executeQuery()) {
                    while (rs.next()) {
                        long   id    = rs.getLong("id");
                        String name  = rs.getString("name");
                        double price = rs.getDouble("price");
                        System.out.printf("  [%d] %s — $%.2f%n", id, name, price);
                    }
                }
            }

        } catch (SQLException e) {
            System.err.println("Database error: " + e.getMessage());
        }
    }
}

Transaction Management

java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class TransactionExample {

    public static void transferFunds(long fromId, long toId, double amount)
            throws SQLException {

        try (Connection conn = DriverManager.getConnection(
                "jdbc:postgresql://localhost:5432/mydb", "app_user", "secret")) {

            // Disable auto-commit to open a manual transaction
            conn.setAutoCommit(false);

            try {
                String debit = "UPDATE accounts SET balance = balance - ? WHERE id = ?";
                try (PreparedStatement ps = conn.prepareStatement(debit)) {
                    ps.setDouble(1, amount);
                    ps.setLong(2, fromId);
                    ps.executeUpdate();
                }

                String credit = "UPDATE accounts SET balance = balance + ? WHERE id = ?";
                try (PreparedStatement ps = conn.prepareStatement(credit)) {
                    ps.setDouble(1, amount);
                    ps.setLong(2, toId);
                    ps.executeUpdate();
                }

                conn.commit();
                System.out.println("Transfer committed successfully.");

            } catch (SQLException e) {
                conn.rollback();
                System.err.println("Transfer rolled back: " + e.getMessage());
                throw e;
            }
        }
    }
}

WeftKitRel — Spring Boot + Spring Data JDBC

application.properties

properties
spring.datasource.url=jdbc:postgresql://localhost:5432/mydb
spring.datasource.username=app_user
spring.datasource.password=secret
spring.datasource.driver-class-name=org.postgresql.Driver

application.yml (alternative)

yaml
spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/mydb
    username: app_user
    password: secret
    driver-class-name: org.postgresql.Driver

JdbcTemplate

java
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Map;

@Repository
public class ProductRepository {

    private final JdbcTemplate jdbc;

    // Spring injects the JdbcTemplate bean backed by the configured DataSource
    public ProductRepository(JdbcTemplate jdbc) {
        this.jdbc = jdbc;
    }

    // --- SELECT returning a list of maps ---
    public List<Map<String, Object>> findCheapProducts(double maxPrice) {
        return jdbc.queryForList(
            "SELECT id, name, price FROM products WHERE price < ? ORDER BY price",
            maxPrice
        );
    }

    // --- SELECT returning a single scalar value ---
    public int countProducts() {
        Integer count = jdbc.queryForObject(
            "SELECT COUNT(*) FROM products",
            Integer.class
        );
        return count != null ? count : 0;
    }

    // --- SELECT mapping rows to a POJO ---
    public List<Product> findByCategory(String category) {
        return jdbc.query(
            "SELECT id, name, price FROM products WHERE category = ?",
            (rs, rowNum) -> new Product(
                rs.getLong("id"),
                rs.getString("name"),
                rs.getDouble("price")
            ),
            category
        );
    }

    // --- INSERT / UPDATE / DELETE ---
    public int createProduct(String name, double price) {
        return jdbc.update(
            "INSERT INTO products (name, price) VALUES (?, ?)",
            name, price
        );
    }

    public int updatePrice(long id, double newPrice) {
        return jdbc.update(
            "UPDATE products SET price = ? WHERE id = ?",
            newPrice, id
        );
    }
}

WeftKitRel — Hibernate / JPA

persistence.xml

xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="https://jakarta.ee/xml/ns/persistence" version="3.0">
  <persistence-unit name="weftkit-pu" transaction-type="RESOURCE_LOCAL">

    <properties>
      <property name="jakarta.persistence.jdbc.driver"
                value="org.postgresql.Driver"/>
      <property name="jakarta.persistence.jdbc.url"
                value="jdbc:postgresql://localhost:5432/mydb"/>
      <property name="jakarta.persistence.jdbc.user"     value="app_user"/>
      <property name="jakarta.persistence.jdbc.password" value="secret"/>

      <!-- Schema management — use "validate" or "none" in production -->
      <property name="hibernate.hbm2ddl.auto"  value="update"/>
      <property name="hibernate.dialect"
                value="org.hibernate.dialect.PostgreSQLDialect"/>
      <property name="hibernate.show_sql"      value="false"/>
    </properties>

  </persistence-unit>
</persistence>

Entity Class

java
import jakarta.persistence.*;

@Entity
@Table(name = "orders")
@NamedQuery(
    name  = "Order.findByStatus",
    query = "SELECT o FROM Order o WHERE o.status = :status ORDER BY o.createdAt DESC"
)
public class Order {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "customer_id", nullable = false)
    private Long customerId;

    @Column(name = "total_amount", nullable = false)
    private Double totalAmount;

    @Column(name = "status", length = 32)
    private String status;

    @Column(name = "created_at", updatable = false)
    private java.time.LocalDateTime createdAt;

    // Standard constructors, getters, and setters
    public Order() {}

    public Order(Long customerId, Double totalAmount, String status) {
        this.customerId   = customerId;
        this.totalAmount  = totalAmount;
        this.status       = status;
        this.createdAt    = java.time.LocalDateTime.now();
    }

    public Long getId()              { return id; }
    public Long getCustomerId()      { return customerId; }
    public Double getTotalAmount()   { return totalAmount; }
    public String getStatus()        { return status; }
    public void setStatus(String s)  { this.status = s; }
}

EntityManager Operations

java
import jakarta.persistence.*;
import java.util.List;

public class OrderDao {

    private final EntityManagerFactory emf =
        Persistence.createEntityManagerFactory("weftkit-pu");

    // --- Persist (INSERT) ---
    public Order create(Long customerId, Double amount) {
        EntityManager em = emf.createEntityManager();
        em.getTransaction().begin();
        try {
            Order order = new Order(customerId, amount, "pending");
            em.persist(order);
            em.getTransaction().commit();
            return order;
        } catch (Exception e) {
            em.getTransaction().rollback();
            throw e;
        } finally {
            em.close();
        }
    }

    // --- Find by primary key ---
    public Order findById(Long id) {
        EntityManager em = emf.createEntityManager();
        try {
            return em.find(Order.class, id);  // returns null if not found
        } finally {
            em.close();
        }
    }

    // --- Merge (UPDATE) ---
    public Order updateStatus(Long id, String newStatus) {
        EntityManager em = emf.createEntityManager();
        em.getTransaction().begin();
        try {
            Order order = em.find(Order.class, id);
            if (order == null) throw new IllegalArgumentException("Order not found: " + id);
            order.setStatus(newStatus);
            Order merged = em.merge(order);
            em.getTransaction().commit();
            return merged;
        } catch (Exception e) {
            em.getTransaction().rollback();
            throw e;
        } finally {
            em.close();
        }
    }

    // --- Remove (DELETE) ---
    public void delete(Long id) {
        EntityManager em = emf.createEntityManager();
        em.getTransaction().begin();
        try {
            Order order = em.find(Order.class, id);
            if (order != null) em.remove(order);
            em.getTransaction().commit();
        } catch (Exception e) {
            em.getTransaction().rollback();
            throw e;
        } finally {
            em.close();
        }
    }

    // --- JPQL query ---
    public List<Order> findByStatus(String status) {
        EntityManager em = emf.createEntityManager();
        try {
            return em.createQuery(
                "SELECT o FROM Order o WHERE o.status = :status ORDER BY o.createdAt DESC",
                Order.class
            )
            .setParameter("status", status)
            .getResultList();
        } finally {
            em.close();
        }
    }

    // --- Named query ---
    public List<Order> findByStatusNamed(String status) {
        EntityManager em = emf.createEntityManager();
        try {
            return em.createNamedQuery("Order.findByStatus", Order.class)
                     .setParameter("status", status)
                     .getResultList();
        } finally {
            em.close();
        }
    }
}

WeftKitRel — Spring Data JPA

application.properties

properties
spring.datasource.url=jdbc:postgresql://localhost:5432/mydb
spring.datasource.username=app_user
spring.datasource.password=secret
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.show-sql=false
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect

Repository Interface

java
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Optional;

@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {

    // Spring Data derives the query from the method name
    List<Order> findByStatus(String status);

    List<Order> findByCustomerIdOrderByCreatedAtDesc(Long customerId);

    Optional<Order> findTopByCustomerIdAndStatus(Long customerId, String status);

    // Custom JPQL query
    @Query("SELECT o FROM Order o WHERE o.totalAmount BETWEEN :min AND :max")
    List<Order> findByAmountRange(@Param("min") double min, @Param("max") double max);

    // Modifying query for bulk updates
    @Modifying
    @Transactional
    @Query("UPDATE Order o SET o.status = :newStatus WHERE o.status = :oldStatus")
    int bulkUpdateStatus(@Param("oldStatus") String oldStatus,
                         @Param("newStatus") String newStatus);
}

Service Using the Repository

java
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
@Transactional(readOnly = true)
public class OrderService {

    private final OrderRepository orders;

    public OrderService(OrderRepository orders) {
        this.orders = orders;
    }

    public List<Order> getPendingOrders() {
        return orders.findByStatus("pending");
    }

    @Transactional
    public Order placeOrder(Long customerId, double amount) {
        Order order = new Order(customerId, amount, "pending");
        return orders.save(order);  // triggers INSERT
    }

    @Transactional
    public Order shipOrder(Long orderId) {
        Order order = orders.findById(orderId)
            .orElseThrow(() -> new IllegalArgumentException("Order not found: " + orderId));
        order.setStatus("shipped");
        return orders.save(order);  // triggers UPDATE
    }
}

WeftKitDoc — MongoDB Java Driver

WeftKitDoc speaks the MongoDB Wire protocol. The standard MongoDB Java Driver connects directly.

Default port: 27017

Connection and Basic CRUD

java
import com.mongodb.client.*;
import com.mongodb.client.model.*;
import org.bson.Document;
import org.bson.conversions.Bson;

import java.util.Arrays;
import java.util.List;

public class DocExample {

    public static void main(String[] args) {
        String connectionString = "mongodb://app_user:secret@localhost:27017/catalog";

        try (MongoClient client = MongoClients.create(connectionString)) {
            MongoDatabase db         = client.getDatabase("catalog");
            MongoCollection<Document> products = db.getCollection("products");

            // --- InsertOne ---
            Document newProduct = new Document("name", "Widget Pro")
                .append("category", "widgets")
                .append("price", 29.99)
                .append("tags", Arrays.asList("sale", "featured"))
                .append("stock", 250);

            products.insertOne(newProduct);
            System.out.println("Inserted id: " + newProduct.getObjectId("_id"));

            // --- InsertMany ---
            List<Document> batch = List.of(
                new Document("name", "Gadget A").append("price", 9.99).append("stock", 100),
                new Document("name", "Gadget B").append("price", 14.99).append("stock", 50)
            );
            products.insertMany(batch);

            // --- Find with Filters ---
            Bson filter = Filters.and(
                Filters.eq("category", "widgets"),
                Filters.gt("price", 10.00)
            );

            try (MongoCursor<Document> cursor = products.find(filter)
                    .sort(Sorts.ascending("price"))
                    .limit(20)
                    .iterator()) {
                while (cursor.hasNext()) {
                    Document doc = cursor.next();
                    System.out.printf("  %s — $%.2f%n",
                        doc.getString("name"), doc.getDouble("price"));
                }
            }

            // --- UpdateOne ---
            products.updateOne(
                Filters.eq("name", "Widget Pro"),
                Updates.combine(
                    Updates.set("price", 24.99),
                    Updates.inc("stock", -10),
                    Updates.addToSet("tags", "clearance")
                )
            );

            // --- DeleteOne ---
            products.deleteOne(Filters.eq("name", "Gadget A"));
        }
    }
}

Aggregation Pipeline

java
import com.mongodb.client.*;
import com.mongodb.client.model.*;
import org.bson.Document;

import java.util.Arrays;
import java.util.List;

public class AggregationExample {

    public static void summarizeByCategory(MongoCollection<Document> products) {

        List<Document> pipeline = Arrays.asList(
            // Stage 1: filter to in-stock items
            Aggregates.match(Filters.gt("stock", 0)).toBsonDocument(),
            // Stage 2: group by category and compute aggregates
            Aggregates.group(
                "$category",
                Accumulators.sum("totalStock", "$stock"),
                Accumulators.avg("avgPrice", "$price"),
                Accumulators.sum("count", 1)
            ).toBsonDocument(),
            // Stage 3: sort by count descending
            Aggregates.sort(Sorts.descending("count")).toBsonDocument()
        );

        // Use typed Document pipeline via the fluent API instead
        AggregateIterable<Document> results = products.aggregate(Arrays.asList(
            Aggregates.match(Filters.gt("stock", 0)),
            Aggregates.group(
                "$category",
                Accumulators.sum("totalStock", "$stock"),
                Accumulators.avg("avgPrice",   "$price"),
                Accumulators.sum("count",       1)
            ),
            Aggregates.sort(Sorts.descending("count"))
        ));

        for (Document result : results) {
            System.out.printf("Category: %s | count=%d | avgPrice=$%.2f%n",
                result.getString("_id"),
                result.getInteger("count"),
                result.getDouble("avgPrice"));
        }
    }
}

WeftKitDoc — Spring Data MongoDB

application.properties

properties
spring.data.mongodb.uri=mongodb://app_user:secret@localhost:27017/catalog

application.yml

yaml
spring:
  data:
    mongodb:
      uri: mongodb://app_user:secret@localhost:27017/catalog

Document Entity and Repository

java
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Query;
import org.springframework.stereotype.Repository;

import java.util.List;

// --- Entity ---
@Document(collection = "products")
public class Product {

    @Id
    private String id;
    private String name;
    private String category;
    private double price;
    private int    stock;

    public Product() {}
    public Product(String name, String category, double price, int stock) {
        this.name     = name;
        this.category = category;
        this.price    = price;
        this.stock    = stock;
    }

    // Getters and setters omitted for brevity
    public String getId()       { return id; }
    public String getName()     { return name; }
    public double getPrice()    { return price; }
    public int    getStock()    { return stock; }
    public String getCategory() { return category; }
}

// --- Repository ---
@Repository
public interface ProductRepository extends MongoRepository<Product, String> {

    List<Product> findByCategory(String category);

    List<Product> findByPriceLessThanOrderByPriceAsc(double maxPrice);

    // Custom query using MongoDB JSON syntax
    @Query("{ 'price': { $gte: ?0, $lte: ?1 }, 'stock': { $gt: 0 } }")
    List<Product> findInPriceRange(double min, double max);
}

MongoTemplate for Complex Queries

java
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class ProductService {

    private final MongoTemplate mongo;
    private final ProductRepository productRepo;

    public ProductService(MongoTemplate mongo, ProductRepository productRepo) {
        this.mongo       = mongo;
        this.productRepo = productRepo;
    }

    public List<Product> findFeaturedWidgets(double maxPrice) {
        Query query = new Query(
            Criteria.where("category").is("widgets")
                    .and("price").lte(maxPrice)
                    .and("stock").gt(0)
        );
        query.limit(10);
        return mongo.find(query, Product.class);
    }

    public void restock(String productId, int quantity) {
        Query  query  = new Query(Criteria.where("id").is(productId));
        Update update = new Update().inc("stock", quantity);
        mongo.updateFirst(query, update, Product.class);
    }

    public Product save(Product product) {
        return productRepo.save(product);
    }
}

WeftKitMem — Jedis (Sync)

WeftKitMem speaks the Redis RESP3 protocol. Jedis is a simple, synchronous Java client.

Default port: 6379

JedisPool Configuration and Basic Commands

java
import redis.clients.jedis.*;

import java.util.Map;

public class JedisExample {

    // JedisPool is thread-safe; create once and share across your application
    private static final JedisPool POOL = new JedisPool(
        new JedisPoolConfig(),
        "localhost",
        6379,
        2000,       // connection timeout (ms)
        "secret"    // password — omit if unauthenticated
    );

    public static void basicOperations() {
        try (Jedis jedis = POOL.getResource()) {

            // SET with expiry
            jedis.set("session:abc123", "user-id:42");
            jedis.expire("session:abc123", 3600L);  // expire in 1 hour

            // SET with options in one call
            jedis.setex("cache:product:99", 300L, "{\"name\":\"Widget Pro\"}");

            // GET
            String value = jedis.get("session:abc123");
            System.out.println("Session value: " + value);

            // EXISTS / DEL
            boolean exists = jedis.exists("session:abc123");
            jedis.del("session:abc123");

            // --- Hash operations ---
            jedis.hset("user:42", Map.of(
                "name",  "Alice",
                "email", "alice@example.com",
                "role",  "admin"
            ));
            String name = jedis.hget("user:42", "name");
            Map<String, String> user = jedis.hgetAll("user:42");
            System.out.println("User: " + user);

            // --- Counter ---
            jedis.incr("stats:page_views");
            jedis.incrBy("stats:bytes_served", 4096L);
        }
    }

    public static void pipelineExample() {
        try (Jedis jedis = POOL.getResource()) {
            Pipeline pipeline = jedis.pipelined();

            // Queue multiple commands without waiting for each response
            for (int i = 0; i < 1000; i++) {
                pipeline.set("key:" + i, "value:" + i);
            }

            // Send all commands and receive responses in one round trip
            pipeline.sync();
            System.out.println("Pipeline: 1000 keys written.");
        }
    }

    public static void shutdown() {
        POOL.close();
    }
}

WeftKitMem — Lettuce (Async)

Lettuce provides fully asynchronous and reactive Redis access backed by Netty.

java
import io.lettuce.core.*;
import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.async.RedisAsyncCommands;
import io.lettuce.core.api.reactive.RedisReactiveCommands;

import java.util.concurrent.CompletableFuture;

public class LettuceExample {

    public static void main(String[] args) throws Exception {

        RedisClient client = RedisClient.create(
            RedisURI.builder()
                .withHost("localhost")
                .withPort(6379)
                .withPassword("secret".toCharArray())
                .withDatabase(0)
                .build()
        );

        try (StatefulRedisConnection<String, String> connection = client.connect()) {

            // --- Async commands ---
            RedisAsyncCommands<String, String> async = connection.async();

            CompletableFuture<String> setFuture = async.set("async:key", "hello").toCompletableFuture();
            CompletableFuture<String> getFuture = setFuture
                .thenCompose(ok -> async.get("async:key").toCompletableFuture());

            String result = getFuture.join();
            System.out.println("Async GET: " + result);

            // --- Reactive commands (Project Reactor) ---
            RedisReactiveCommands<String, String> reactive = connection.reactive();

            reactive.set("reactive:key", "world")
                .then(reactive.get("reactive:key"))
                .subscribe(v -> System.out.println("Reactive GET: " + v));

            Thread.sleep(500);  // wait for reactive pipeline
        }

        client.shutdown();
    }
}

WeftKitMem — Spring Data Redis

application.properties

properties
spring.data.redis.host=localhost
spring.data.redis.port=6379
spring.data.redis.password=secret
spring.data.redis.timeout=2000ms

RedisTemplate Bean and Operations

java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        return template;
    }
}
java
import org.springframework.data.redis.core.*;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.util.List;
import java.util.Map;

@Service
public class CacheService {

    private final RedisTemplate<String, Object> redis;

    public CacheService(RedisTemplate<String, Object> redis) {
        this.redis = redis;
    }

    // --- Value (String) operations ---
    public void cacheProduct(String id, Object product) {
        ValueOperations<String, Object> values = redis.opsForValue();
        values.set("product:" + id, product, Duration.ofMinutes(10));
    }

    public Object getProduct(String id) {
        return redis.opsForValue().get("product:" + id);
    }

    // --- Hash operations ---
    public void setUserFields(String userId, Map<String, Object> fields) {
        redis.opsForHash().putAll("user:" + userId, fields);
        redis.expire("user:" + userId, Duration.ofHours(1));
    }

    public Object getUserField(String userId, String field) {
        return redis.opsForHash().get("user:" + userId, field);
    }

    // --- List operations ---
    public void pushToQueue(String queueName, Object item) {
        redis.opsForList().rightPush(queueName, item);
    }

    public List<Object> drainQueue(String queueName, long count) {
        return redis.opsForList().range(queueName, 0, count - 1);
    }
}

WeftKitGraph — Neo4j Java Driver

WeftKitGraph speaks the Bolt protocol. The standard Neo4j Java Driver connects without modification.

Default port: 7687

Driver Setup and Session Usage

java
import org.neo4j.driver.*;
import org.neo4j.driver.summary.ResultSummary;

import java.util.List;
import java.util.Map;

public class GraphExample implements AutoCloseable {

    private final Driver driver;

    public GraphExample(String uri, String user, String password) {
        this.driver = GraphDatabase.driver(
            uri,
            AuthTokens.basic(user, password),
            Config.builder()
                .withMaxConnectionPoolSize(50)
                .withConnectionAcquisitionTimeout(30, java.util.concurrent.TimeUnit.SECONDS)
                .build()
        );
        driver.verifyConnectivity();
    }

    // --- Write transaction: CREATE nodes and relationships ---
    public void createUserFollows(String fromName, String toName) {
        try (Session session = driver.session()) {
            session.executeWrite(tx -> {
                tx.run(
                    "MERGE (a:User {name: $from}) " +
                    "MERGE (b:User {name: $to}) "  +
                    "MERGE (a)-[:FOLLOWS]->(b)",
                    Map.of("from", fromName, "to", toName)
                );
                return null;
            });
        }
    }

    // --- Read transaction: traverse relationships ---
    public List<String> getFollowers(String userName) {
        try (Session session = driver.session()) {
            return session.executeRead(tx -> {
                Result result = tx.run(
                    "MATCH (follower:User)-[:FOLLOWS]->(u:User {name: $name}) " +
                    "RETURN follower.name AS followerName " +
                    "ORDER BY followerName",
                    Map.of("name", userName)
                );
                return result.list(r -> r.get("followerName").asString());
            });
        }
    }

    // --- Read: shortest path between two users ---
    public int shortestPathLength(String fromName, String toName) {
        try (Session session = driver.session()) {
            return session.executeRead(tx -> {
                Result result = tx.run(
                    "MATCH p = shortestPath(" +
                    "  (a:User {name: $from})-[:FOLLOWS*]-(b:User {name: $to})" +
                    ") RETURN length(p) AS hops",
                    Map.of("from", fromName, "to", toName)
                );
                return result.hasNext() ? result.single().get("hops").asInt() : -1;
            });
        }
    }

    // --- Write: delete a relationship ---
    public void unfollow(String fromName, String toName) {
        try (Session session = driver.session()) {
            session.executeWrite(tx -> {
                ResultSummary summary = tx.run(
                    "MATCH (a:User {name: $from})-[r:FOLLOWS]->(b:User {name: $to}) DELETE r",
                    Map.of("from", fromName, "to", toName)
                ).consume();
                System.out.println("Relationships deleted: " + summary.counters().relationshipsDeleted());
                return null;
            });
        }
    }

    @Override
    public void close() {
        driver.close();
    }

    public static void main(String[] args) {
        try (GraphExample graph = new GraphExample("bolt://localhost:7687", "neo4j", "secret")) {
            graph.createUserFollows("Alice", "Bob");
            graph.createUserFollows("Alice", "Carol");
            graph.createUserFollows("Bob",   "Carol");

            List<String> followers = graph.getFollowers("Carol");
            System.out.println("Carol's followers: " + followers);

            int hops = graph.shortestPathLength("Alice", "Carol");
            System.out.println("Shortest path Alice -> Carol: " + hops + " hop(s)");
        }
    }
}

WeftKitKV — AWS SDK for Java v2

WeftKitKV exposes a DynamoDB REST API. Point the AWS SDK at WeftKit's endpoint instead of AWS.

Default port: 8000

Client Setup and Item Operations

java
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.*;

import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class KeyValueExample {

    private static final DynamoDbClient dynamo = DynamoDbClient.builder()
        .endpointOverride(URI.create("http://localhost:8000"))
        .region(Region.US_EAST_1)   // any value works; WeftKit ignores it
        .credentialsProvider(StaticCredentialsProvider.create(
            AwsBasicCredentials.create("weftkit", "secret")
        ))
        .build();

    // --- PutItem (INSERT or full REPLACE) ---
    public static void putProduct(String productId, String name, double price, int stock) {
        Map<String, AttributeValue> item = new HashMap<>();
        item.put("productId", AttributeValue.fromS(productId));
        item.put("name",      AttributeValue.fromS(name));
        item.put("price",     AttributeValue.fromN(String.valueOf(price)));
        item.put("stock",     AttributeValue.fromN(String.valueOf(stock)));

        dynamo.putItem(PutItemRequest.builder()
            .tableName("products")
            .item(item)
            .build());

        System.out.println("PutItem succeeded for: " + productId);
    }

    // --- GetItem ---
    public static Map<String, AttributeValue> getProduct(String productId) {
        GetItemResponse response = dynamo.getItem(GetItemRequest.builder()
            .tableName("products")
            .key(Map.of("productId", AttributeValue.fromS(productId)))
            .build());

        if (!response.hasItem()) {
            System.out.println("Item not found: " + productId);
            return Map.of();
        }

        Map<String, AttributeValue> item = response.item();
        System.out.printf("Found: %s | price=%s | stock=%s%n",
            item.get("name").s(),
            item.get("price").n(),
            item.get("stock").n());
        return item;
    }

    // --- UpdateItem (partial update) ---
    public static void decrementStock(String productId, int quantity) {
        dynamo.updateItem(UpdateItemRequest.builder()
            .tableName("products")
            .key(Map.of("productId", AttributeValue.fromS(productId)))
            .updateExpression("SET stock = stock - :qty")
            .conditionExpression("stock >= :qty")
            .expressionAttributeValues(Map.of(
                ":qty", AttributeValue.fromN(String.valueOf(quantity))
            ))
            .build());
    }

    // --- DeleteItem ---
    public static void deleteProduct(String productId) {
        dynamo.deleteItem(DeleteItemRequest.builder()
            .tableName("products")
            .key(Map.of("productId", AttributeValue.fromS(productId)))
            .build());
        System.out.println("Deleted: " + productId);
    }

    // --- Query with KeyConditionExpression ---
    // Assumes table has partition key "category" and sort key "productId"
    public static List<Map<String, AttributeValue>> queryByCategory(String category) {
        QueryResponse response = dynamo.query(QueryRequest.builder()
            .tableName("products")
            .keyConditionExpression("category = :cat")
            .expressionAttributeValues(Map.of(
                ":cat", AttributeValue.fromS(category)
            ))
            .limit(100)
            .build());

        System.out.println("Items found: " + response.count());
        return response.items();
    }

    // --- Query with sort key range ---
    public static List<Map<String, AttributeValue>> queryByPriceRange(
            String category, double minPrice, double maxPrice) {

        QueryResponse response = dynamo.query(QueryRequest.builder()
            .tableName("products")
            .keyConditionExpression("category = :cat AND price BETWEEN :min AND :max")
            .expressionAttributeValues(Map.of(
                ":cat", AttributeValue.fromS(category),
                ":min", AttributeValue.fromN(String.valueOf(minPrice)),
                ":max", AttributeValue.fromN(String.valueOf(maxPrice))
            ))
            .build());

        return response.items();
    }

    public static void main(String[] args) {
        putProduct("prod-001", "Widget Pro",  29.99, 250);
        putProduct("prod-002", "Gadget Ultra", 99.99, 50);

        getProduct("prod-001");
        decrementStock("prod-001", 10);

        List<Map<String, AttributeValue>> items = queryByCategory("widgets");
        items.forEach(item -> System.out.println(item.get("name").s()));

        deleteProduct("prod-002");
    }
}

Connection String Reference

| Engine | Driver | Connection String Format | |---|---|---| | WeftKitRel | JDBC / Spring / Hibernate | jdbc:postgresql://localhost:5432/mydb | | WeftKitDoc | MongoDB Java Driver | mongodb://user:pass@localhost:27017/catalog | | WeftKitMem | Jedis / Lettuce | redis://:secret@localhost:6379/0 | | WeftKitGraph | Neo4j Java Driver | bolt://localhost:7687 | | WeftKitKV | AWS SDK v2 | endpointOverride("http://localhost:8000") |

All ports are configurable in weftkit.toml. See the Integration Guides overview for TLS/SSL and authentication details.