0

我正在开发一个 Java 项目,使用 Hibernate 来管理 SQL 数据库上的数据。我尝试从数据库中获取实例列表,这些实例具有它们共享的组的最小时间戳。该组由容器建模。

这是一个最小的模型草图:

@Entity
@Table(name = "object")
public class Object implements Serializable{

    @Id
    @GeneratedValue(strategy = GenerationType.Auto) 
    long obj_id;

    @Column(name = "time_stamp", nullable = false)
    Date timestamp;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "container_id", nullable = false)
    Container con;
}

@Entity
@Table(name = "container")
public class Container{

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    long  con_id;

    @OneToMany(mappedBy = "container")
    List<object> obj_list;
}

所以有一些带有时间戳的对象和对这些对象进行分组的容器。

例如,有两个容器,con_a 和 con_b:

Container con_a:
    con_id = 1
    obj_list = {obj_a, obj_b}

Container con_b:
    con_id = 2
    obj_list = {obj_c}

以及三个对象,obj_a、obj_b、obj_c:

Object obj_a
    obj_id = 1
    timestamp = 10
    con = con_a

Object obj_b
    obj_id = 2
    timestamp = 20
    con = con_a

Object obj_c
    obj_id = 3
    timestamp = 30
    con = con_b

此示例中所需的列表如下所示:

List<Object> = {obj_a, obj_c}

我似乎在转圈,因为我什至不知道从哪里“开始”查询:

Criteria crit = session.createCriteria(Container.class). ...

或者

Criteria crit = session.createCriteria(Object.class). ...

这对我来说似乎都是可能的,但我只是不知道如何从这两种可能性中继续下去。

更新 [2014.07.11, 14:19]:

我尝试使用 Object 类开始查询并使用了子查询:

Session session = getSession();
Transaction transaction = session.beginTransaction();

DetachedCriteria IdListOfGroupMinimum = DetachedCriteria.forClass(Object.class, "obj")

IdListOfGroupMinimum.createAlias("con.id", "containerId")
    .setProjection(
     .Projections.projectionList()
     .add(Projections.property("obj.id"))
     .add(Projections.min("obj.timestamp"))
     .add(Projections.groupProperty("containerId")))
    .setProjection(Projection.property("obj.id"));

Criteria objects = session.createCriteria(object.class, "obj")
objects.add(Subqueries.in("obj.id", IdListOfGroupMinimum));

List<Object> = objects.list();

但我收到以下错误:

javax.servlet.ServletException:org.hibernate.QueryException:不是关联:id

我试图这样做:

SELECT * from Object
WHERE id IN (
    SELECT obj.id
    FROM Object obj
    INNER JOIN (
        SELECT obj.containerID, MIN(obj.timestamp) AS minimum 
        FROM Object obj 
        GROUP BY obj.containerID) subquery
    ON obj.containerID = subquery.containerID
    WHERE obj.timestamp = subquery.minimum
    )
4

1 回答 1

0

我为我的问题找到了一个解决方案,它可能不是最优雅的解决方案,但它确实有效。

主要是我使用了上面已经发布的 SQL-Query:

 Session session = getSession();
 Transaction transaction = session.beginTransaction();

 //This query fetches the IDs of the smallest objects in each group with 
 //regard to the timestamp
 Query q = session.createSQLQuery(
                "SELECT obj.id FROM Object obj "
              + "INNER JOIN ( " 
                   + "SELECT obj.containerID, MIN(obj.timestamp) AS minimum "
                   + "FROM Object obj "
                   + "GROUP BY obj.containerID) subquery "
              + "ON obj.containerID = subquery.containerID "
              + "WHERE obj.timestamp = subquery.minimum "
              );

//This tells Hibernate that the result are values of type Long
q.addScalar("id", LongType.INSTANCE)

//Creates a list of the found IDs
@SuppressWarnings("unchecked")
List<Long> ids = q.list(); 

//Fetches all object with those IDs...
Criteria smallestOfEachGroup = session.createCriteria(Object.class)
                                      .add(Restrictions.in("id", ids);
//...and saves them in a list.
@SuppressWarnings("unchecked")
List<Object> desiredList = smallestOfEachGroup.list()  

try{
    transaction.commit();
} catch(HibernateException e) {
    transaction.rollback();
}

由于我所有的草图都不是真正的代码,所以可能仍然存在命名错误。

无论如何,我希望这对某人有所帮助。

我仍然会对任何更优雅的解决方案感到满意。

更新 [2014.07.20, 18:50]:

我找到了一个专门使用 Hibernate Criteria 的解决方案:)

Session session = getSession();
Transaction transaction = session.beginTransaction();

//This subquery fetches the minimal timestamp of a container.
DetachedCriteria minOfGroup = DetachedCriteria.forClass(Object.class);
minOfGroup.add(Restrictions.eqProperty("con.con_id", "outerObject.con.con_id")
          .setProjection(Projections.min("timestamp"));

//This subquery fetches the IDs of all Objects, whose timestamp is minimal
//in their container.
DetachedCriteria groupwiseMin = DetachedCriteria.forClass(Object.class, "outerObject");
groupwiseMin.add(Subqueries.propertyEq("timestamp", minOfGroup));
            .setProjections(Projections.id())

//This subquery fetches all Objects whose IDs are fetched by the groupwiseMin
//query
Criteria groupwiseMinObjects = session.createCriteria(Object.class);
groupwiseMinObjects.add(Subqueries.propertyIn("obj_id", groupwiseMin));

List<Object> desiredObjects = groupwiseMinObjects.list();

try{
   transaction.commit();
} catch(HibernateException e) {
   transaction.rollback();
}

我认为您可以使此查询更短,如果您删除上面的 groupwiseMinObjects 查询,请将 groupwiseMin 查询替换为:

Criteria anotherGroupWiseMinObjects = session.createCriteria(Object.class, "outerObject");
anotherGroupwiseMinObjects.add(Subqueries.propertyEq("timestamp", minOfGroup));

但我没有测试。在我的原始项目中,我使用了几个子查询,它们汇聚在一个查询中。这意味着在一些子查询之后,会有一个最终查询,例如:

Criteria finalQuery = session.createCriteria(Object.class);
finalQuery.add(Subqueries. (...) )
          (...)
          .add(Subqueries. (...) );
于 2014-07-16T20:34:32.183 回答