Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Stardust

nebulae2us edited this page · 28 revisions
Clone this wiki locally

Stardust is a SQL-centric ORM framework for Java. You love SQL, but don't like to write a lot of boilerplate codes. Stardust is for you. Stardust aims to ease the developers on the job of the mapping between relational data and Java model objects, but does not take away the developers' control over SQL. It automatically generates SQL in most cases, but also allows developers to plug their own SQL and still happily maps the data back into Java model objects.

For example, to get list of Course for a Student, the developer can do:

Student student = daoManager.newQuery(Student.class)
    .outerJoin("courses", "c")
    .filterById(studentId)
    .uniqueValue();

or if the SQL is more complex, the developer can also do:

String sql = "select student_id, first_name, last_name, s.course_id, c.course_name as c_course_name " +
             "  from student s left outer join student_course sc on (s.student_id = sc.student_id) " +
             "                 left outer join course c on (sc.course_id = c.course_id)";

Student student = daoManager.newQuery(Student.class)
    .outerJoin("courses", "c")
    .backedBySql(sql)
    .filterById(studentId)
    .uniqueValue(); 

Behind the scene, the DaoManager has 2 separate engines: SQL generation and data mapping. In the first example, it automatically generates the SQL, then executes the SQL, and maps the result to create the Java model object Student. In the second example, it does not generate the SQL from scratch. Instead, it creates new SQL based on the supplied SQL and additional predicates, then executes the new SQL, and maps the result to create the Java model object.

Stardust also provides JdbcExecutor to help you to execute SQLs without worrying about many boilerplate codes:

Long one = jdbcExecutor.queryForLong("select 1 from dual"); // dual is Oracle's one-row table.

// to get a list of Student's IDs:
List<Long> studentIds = jdbcExecutor.queryForListOfLong("select studentId from student");

License

Apache License, Version 2.0

Getting Started

Download the JAR files:

Usage

The core services in Stardust are DaoManager and JdbcExecutor. DaoManager lets you build query and update database using Java model objects while JdbcExecutor lets you execute SQL. Behind the scene, DaoManager uses JdbcExecutor to execute SQL, and maps the ResultSet to Java models. Both JdbcExecutor and DaoManager are thread safe. They can be used as singleton.

// Assumption: you already have dataSource.
// Instantiate JdbcExecutor
Dialect dialect = new OracleDialect();
JdbcExecutor jdbcExecutor = new JdbcExecutor(dataSource, dialect);

// Instantiate DaoManager
DaoManager daoManager = new DaoManager(jdbcExecutor);

// Scan the package where the entity models are. Entity models are defined using JPA annotations.
daoManager.setPackageToScan("com.example.model");

JdbcExecutor

JdbcExecutor eases the job of executing SQL without a lot of boilerplate code. There are multiple ways to execute SELECT SQL:

// get mileage for a car with id=1. If the mileage is null, the result is null
Long mileage = jdbcExecutor.queryForLong("select mileage from car where car_id = ?", Arrays.asList(1));

// get car ID list of type Sedan
Map<String, String> param = Collections.singletonMap("carType", "Sedan");
List<Long> carIds = jdbcExecutor.queryForListOfLong("select car_id from car where car_type = :carType", param);

// get list of Sedan car
List<Car> cars = jdbcExecutor.query("select car_id, model, make, mileage where car_type = :carType", param,
        new RecordMapper<Car>() {
            @Override
            public Car mapRecord(DataReader dataReader) {
                Long carId = dataReader.readLong("car_id");
                String model = dataReader.readString("model");
                String make = dataReader.readString("make");
                Long mileage = dataReader.readLong("mileage");

                Car car = new Car(carId, model, make, mileage);
                return car;
            }
        }
    );

To update database:

jdbcExecutor.update("insert into Car (car_id, model, make, mileage, car_type) values (?, ?, ?, ?, ?)",
    Arrays.asList(1, "Toyota", "Camry", 20000, "Sedan"));

jdbcExecutor.update("delete from Car where car_id = :carId", Collections.singletonMap("carId", 1));

DaoManager

DaoManager is the heart of the ORM framework. While JdbcExecutor makes it easy to execute SQL, it's cumbersome to map the result to Java model objects. Like JPA, DaoManager automatically generates SQL and maps the ResultSet to Java models. Unlike JPA, Stardust realizes that some SQLs can be very complex for your application, and allows you to plug your own SQL when necessary.

Let's say we have model class:

class Car {
    @Id
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="car_seq")
    @SequenceGenerator(name="store_seq", sequenceName="car_seq")
    private Long carId;

    private String model;
    private String make;
    private Long mileage;
    private String carType;

    @ManyToOne private Store store;

    // getter and setter
}

class Store {
    @Id
    @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="store_seq")
    @SequenceGenerator(name="store_seq", sequenceName="store_seq")
    private Long storeId;

    @Embedded private Address address;
    @OneToMany(mappedBy="store") private List<Car> cars;

    // getter and setter
}

class Address {
    private String streetAddress;
    private String city;
    private String state;

    // getter and setter
}

There are multiple ways to query data using DaoManager:

// get the car with id = 1
Car result = daoManager.get(Car.class, 1);

// get the first 10 cars ordering by mileage
List<Car> result = daoManager
    .newQuery(Car.class)
    .orderBy("mileage")
    .maxResults(10)
    .list();

// get store of id = 100, and its cars sorted by mileage
Store result = daoManager
    .newQuery(Store.class)
    .outerJoin("cars", "c")
    .filterBy("storeId = ?", 100)
    .orderBy("c.mileage")
    .uniqueValue();

// Query using subquery: First construct subQuery to return list of store IDs in Illinois
Query<?> subQuery = daoManager
    .newQuery(Store.class)
    .select("storeId")
    .filterBy("address.state = ?", "IL")
    .toQuery();

// then query for cars that are in those stores:
List<Car> result = daoManager
    .newQuery(Car.class)
    .filterBy("carId in (?)", subQuery)
    .list();

The result from DaoManager's queries are real POJOs. They are not proxy objects, hence you can use these objects without side effects. Also, these POJOs can be transferred through different layers without exception like Hibernate's LazyInitailizationException.

To manipulate database:

// to create new store
Store store = new Store();
store.setAddress(new Address("street address", "Chicago", "IL"));
daoManager.save(store);

// to update existing store
Store store = daoManager.get(Store.class, 100);
store.setAddress(new Address("new address", "Chicago", "IL");
daoManager.update(store);

// to delete a store
Store store = daoManager.get(Store.class, 100);
daoManager.delete(store);

If you want to use your own SQL for query instead of Stardust's automatically generated SQL, you can do so following some rules of naming column for the SQL result:

// to get the car of id = 1
Car car = daoManager
    .newQuery(Car.class)
    .backedBySql("select car_id, model, make, mileage, car_type from car")
    .filterBy("carId = ?", 1)
    .uniqueValue();

The next example is to get list of cars (with store details) where the store is located in Illinois. This is a SQL we would like to use (this SQL is mainly for demonstration purpose; in reality, you may have a complex SQL that cannot be automatically generated):

select c.car_id, c.model, c.make, c.mileage, c.store_id,
       s.street_address s_street_address, s.city s_city, s.state s_state
  from car c
           inner join store s
               on (c.store_id = s.store_id)
// to get a list of cars whose stores are located in Illinois:
List<Car> result = daoManager
    .newQuery(Car.class)
    .innerJoin("store", "s")
    .backedBySql(theSql)
    .filterBy("s.address.state = ?", "IL")
    .list();

Issues

Issues

Something went wrong with that request. Please try again.