spBee - Core

In the core module the Annotation Processor is defined and it’s where the magic happens. The magic behind this is separated into two steps:

Collect Data

The data is collected by the three classes in the at.rseiler.spbee.core.collector package.

DtoCollector

Collects the data of the annotated @Dto interfaces and classes.

  • gets all methods annotated with @StoredProcedure
    • extracts the method name
    • extracts the return type
    • collects all parameters
    • collects all non spBee annotations
    • reads the @StoredProcedure annotation
    • reads the @RowMapper annotation
    • reads the @ReturnNull annotation
    • reads the @MappingConstructor annotation
  • with this data a DtoClass object is created

EntityClassCollector

Collects the data of the annotated @Entity classes.

  • collects the constructors annotated with @MappingConstructor. If no constructor is annotated with @MappingConstructor then the public constructor is used.
    • collects all parameters
    • reads the @MappingConstructor annotation
  • with this data a MapperClass object is created

ResultSetCollector

Collects the data of the annotated @ResultSet classes.

  • collects all class variables
    • reads the name and the type
  • with this data a ResultSetVariable object is created

Generate Code

The code is generated by the three classes in the at.rseiler.spbee.core.generator package. The generators uses the Codemodel library to build the Java source code.

DtoGenerator

Generates the code for the DTO classes. All collected non spBee annotations will be put on the corresponding methods.

The DTO methods have 5 flavors:

The return type is a list of entities

public List<Entity> getUsers() {
    Object interceptorIdObject = SpInterceptor.before("sp_get_user"); // optional
    List<Entity> list = ((List<Entity>) sp.execute().get("#result-set-0"));
    SpInterceptor.after(interceptorIdObject, "sp_get_user"); // optional
    return list;
}

The return type is an entity

public Entity getEntity(int id) {
    Object interceptorIdObject = SpInterceptor.before("sp_get_user"); // optional
    List<Entity> list = ((List<Entity>) spGetUser.execute(id).get("#result-set-0"));
    SpInterceptor.after(interceptorIdObject, "sp_get_user"); // optional

    if (list.size() == 1) {
        return list.get(0);
    } else {
        if (list.size() == 0) {
            throw new ObjectDoesNotExist();
        } else {
            throw new MultipleObjectsReturned();
        }
    }
}

The return type is an optional type of an entity

public Optional<Entity> getOptionalEntity(int id) {
    Object interceptorIdObject = SpInterceptor.before("sp_get_user"); // optional
    List<Entity> list = ((List<Entity> ) spGetUser.execute(id).get("#result-set-0"));
    SpInterceptor.after(interceptorIdObject, "sp_get_user"); // optional

    if (list.size() == 1) {
        return Optional.of(list.get(0));
    } else {
        if (list.size() == 0) {
            return Optional.empty();
        } else {
            throw new MultipleObjectsReturned();
        }
    }
}

The return type is an entity and the method is annotated with @ReturnNull

public Entity getEntityOrNull(int id) {
    Object interceptorIdObject = SpInterceptor.before("sp_get_user"); // optional
    List<Entity> list = ((List<Entity>) spGetUser.execute(id).get("#result-set-0"));
    SpInterceptor.after(interceptorIdObject, "sp_get_user"); // optional

    if (list.size() == 1) {
        return list.get(0);
    } else {
        if (list.size() == 0) {
            return null;
        } else {
            throw new MultipleObjectsReturned();
        }
    }
}

The return type is a ResultSet

public UserPermissionsResultSet getUserWithPermissions(int id) {
    Object interceptorIdObject = SpInterceptor.before("sp_get_user"); // optional
    Map<String, Object> map = spGetUserWithPermissions.execute(id);
    SpInterceptor.after(interceptorIdObject, "sp_get_user"); // optional
    List<Entity> list0 = ((List<Entity> ) map.get("#result-set-0"));
    Optional obj0;

    if (list0.size() == 1) {
        obj0 = Optional.of(list0.get(0));
    } else {
        if (list0.size() == 0) {
            obj0 = Optional.empty();
        } else {
            throw new MultipleObjectsReturned();
        }
    }

    List<Permission> list1 = ((List<Permission>) map.get("#result-set-1"));
    return new UserPermissionsResultSet(obj0, list1);
}

MapperGenerator

Generates the code for the MapperClasses. The Entity’s constructor must only have basic parameters. It’s not possible to use other entities in the constructor. If a more complex mapping is required then write an own MapperClass and use the @RowMapper annotation.

public class EntityMapper implements RowMapper<Entity> {

     public Entity mapRow(ResultSet rs, int rowNum) throws SQLException {
         return new Entity(rs.*(1), rs.*(2));
     }

}

StoredProcedureGenerator

Generates the code for the StoredProcedure classes.

Single result set

public class SpGetEntity extends StoredProcedure {

    public SpGetEntity(DataSource dataSource) {
        super(dataSource, "sp_get_entity");
        declareParameter(new SqlParameter("id", Types.INTEGER));
        declareParameter(new SqlReturnResultSet("#result-set-0", new EntityDefaultMapper()));
        compile();
    }

    public Map<String, Object> execute(int id) {
        return super.execute(id);
    }

}

Multiple result sets

public class SpGetEntity extends StoredProcedure {

    public SpGetEntity(DataSource dataSource) {
        super(dataSource, "sp_get_entity");
        declareParameter(new SqlParameter("id", Types.INTEGER));
        declareParameter(new SqlReturnResultSet("#result-set-0", new Entity1DefaultMapper()));
        declareParameter(new SqlReturnResultSet("#result-set-1", new Entity2DefaultMapper()));
        compile();
    }

    public Map<String, Object> execute(int id) {
        return super.execute(id);
    }

}