使用URL键/值样式参数的Hibernate查询限制

时间:2012-09-24 00:55:13

标签: java hibernate tapestry

我正在使用Tapestry5和Hibernate。我正在尝试构建一个使用URL生成的动态限制的条件查询。我的URL上下文设计为键/值对。

示例

www.mywebsite.com/make/ford/model/focus/year/2009

我按照以下方式解码参数

private Map<String, String> queryParameters;
private List<Vehicle> vehicles;

    void onActivate(EventContext context) {
            //Count is 6 - make/ford/model/focus/year/2009
            int count = context.getCount();

            if (count > 0) {
                int i;
                for (i = 0; (i + 1) < count; i += 2) {
                    String name = context.get(String.class, i);
                    String value = context.get(String.class, i + 1);

                    example "make"
                    System.out.println("name " + name);

                    example "ford"
                    System.out.println("value " + value);

                    this.queryParameters.put(name, value);
                }
            }  

            this.vehicles = this.session.createCriteria(Vehicle.class)
...add dynamic restrictions. 
        }

我希望有人可以帮我弄清楚如何动态地将限制列表添加到我的查询中。我确信这已经完成,所以如果有人知道帖子,那也会有所帮助。感谢

4 个答案:

答案 0 :(得分:2)

正如另一个答案所说的那样,但这里更详细说明。我认为你问题的关键在于“告诉我如何添加限制”。无论如何,这是我的解释。

您需要将每个限制解码到自己的字段中。

您需要知道每个字段的Java实体属性名称。

然后构建这两件事的Map,关键是已知的静态Java实体属性名称,值是URL解码数据(可能带有类型转换)。

private Map<String, Object> queryParameters;
private List<Vehicle> vehicles;

void onActivate(EventContext context) {
        //Count is 6 - make/ford/model/focus/year/2009
        int count = context.getCount();

        queryParameters = new HashMap<String,Object>();
        if (count > 0) {
            int i;
            for (i = 0; (i + 1) < count; i += 2) {
                String name = context.get(String.class, i);
                String value = context.get(String.class, i + 1);

                Object sqlValue = value;
                if("foobar".equals(name)) {
                    // sometime you don't want a String type for SQL compasition
                    //  so convert it
                    sqlValue = UtilityClass.doTypeConversionForFoobar(value);
                } else if("search".equals(name) ||
                          "model".equals(name) ||
                          "year".equals(name)) {
                    // no-op this is valid 'name'
                } else if("make".equals(name)) {
                    // this is a suggestion depends on your project conf
                    name = "vehicleMake.name";
                } else {
                    continue;  // ignore values we did not expect
                }
                // FIXME: You should validate all 'name' values 
                // to be valid and/or convert to Java property names here

                System.out.println("name " + name);
                System.out.println("value " + value);

                this.queryParameters.put(name, sqlValue);
            }
        }  

        Criteria crit = this.session.createCriteria(Vehicle.class)
        for(Map.Entry<String,Object> e : this.queryParameters.entrySet()) {
            String n = e.getKey();
            Object v = e.getValue();
            // Sometimes you don't want a direct compare 'Restructions.eq()'
            if("search".equals(n))
                crit.add(Restrictions.like(n, "%" + v + "%"));
            else  // Most of the time you do
                crit.add(Restrictions.eq(n, v));
        }

        this.vehicles = crit.list();  // run query
    }

另见https://docs.jboss.org/hibernate/orm/3.5/reference/en/html/querycriteria.html

如上所述,不应该存在SQL注入的风险,因为“name”和“n”部分应该针对已知的良好列表进行100%验证。 “value”和“v”被正确转义,就像使用SQL位置占位符'?'。

E&安培; OE

答案 1 :(得分:1)

我认为你只需循环参数Map并为每对添加一个限制。

请注意,如果您不小心,这将打开您的SQL注入攻击。防止这种情况的最简单方法是在添加到Criteria之前检查已知Vehicle属性的键。

答案 2 :(得分:1)

另一种选择是通过从名称/值对构建对象来创建示例查询:

Vehicle vehicle = new Vehicle();
int count = context.getCount();
int i;
for (i = 0; (i + 1) < count; i += 2) {
  String name = context.get(String.class, i);
  String value = context.get(String.class, i + 1);
  // This will call the setter for the name, passing the value
  // So if name is 'make' and value is 'ford', it will call vehicle.setMake('ford')
  BeantUtils.setProperty(vehicle, name, value);
}

// This is using a Hibernate example query:
vehicles = session.createCriteria(Vehicle.class).add(Example.create(vehicle)).list();

有关详细信息,请参阅BeanUtils.setPropertyExample Queries

假设您每个属性只允许一个值,并且查询参数正确映射到属性名称。可能还有需要考虑的转换问题,但我认为setProperty可以处理常见问题。

答案 3 :(得分:1)

如果它们是查询参数,则应将它们视为查询参数而不是路径参数。您的网址应如下所示:

www.mywebsite.com/vehicles?make=ford&model=focus&year=2009

您的代码应如下所示:

public class Vehicles {

@ActivationRequestParameter
private String make;

@ActivationRequestParameter
private String model;

@ActivationRequestParameter
private String year;

@Inject
private Session session;

@OnEvent(EventConstants.ACTIVATE)
void activate() {
    Criteria criteria = session.createCriteria(Vehicle.class);

    if (make != null) criteria.add(Restrictions.eq("make", make));
    if (model != null) criteria.add(Restrictions.eq("model", model));
    if (year != null) criteria.add(Restrictions.eq("year", year));

    vehicles = criteria.list();
}

}

假设您正在使用Grid组件显示车辆,我强烈建议您使用HibernateGridDataSource而不是在“activate”事件处理程序中进行查询。

public class Vehicles {

    @ActivationRequestParameter
    private String make;

    @ActivationRequestParameter
    private String model;

    @ActivationRequestParameter
    private String year;

    @Inject
    private Session session;

    @OnEvent(EventConstants.ACTIVATE)
    void activate() {
    }

    public GridDataSource getVehicles() {
        return new HibernateGridDataSource(session, Vehicles.class) {
            @Override
            protected void applyAdditionalConstraints(Criteria criteria) {
                if (make != null) criteria.add(Restrictions.eq("make", make));
                if (model != null) criteria.add(Restrictions.eq("model", model));
                if (year != null) criteria.add(Restrictions.eq("year", year));
            }
        };
    }
}