前面章节在没有介绍 OpenJPA 的占位符之前,我们创建本地查询时,如果存在条件,直接使用“+”符号进行 SQL 语句字符串连接。示例代码如下:
int age = 50; float salary = 4000.0f; Query query = em.createNativeQuery( "select id, name, age, salary from user " + " where age < " + age + " and salary > " + salary, User.class); List<User> list = query.getResultList(); for(User user : list) { System.out.println(user); }
上面拼接 SQL 语句的方式容易出现 SQL 注入问题。那什么是 SQL 注入呢?
SQL注入指 web 应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在 web 应用程序中事先定义好的查询语句的结尾上添加额外的 SQL 语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。
非常庆幸的是,OpenJPA 中提供了三种方式的占位符,分别如下:
?:传统 JDBC 方式的占位符,仅仅适应本地SQL查询;
?n:传统 ? 占位符的改良占位符,仅仅适应JPQL查询
?name:变量名形式的占位符,仅仅适应JPQL查询
该种占位符和传统 JDBC 占位符一致,仅仅用于本地 SQL 查询。代码如下:
Query query = em.createNativeQuery( "select id, name, age, salary from user " + " where age < ? and salary > ?", User.class); query.setParameter(1, 50); query.setParameter(2, 4000.0f); List<User> list = query.getResultList(); for(User user : list) { System.out.println(user); }
输出 SQL 日志如下:
select id, name, age, salary from user where age < ? and salary > ? [params=?, ?] User{id=3, name='用户-2', age=44, salary=5855.36} User{id=4, name='用户-3', age=10, salary=4105.1895} User{id=10, name='用户-9', age=26, salary=6327.472}
注意:参数位置必须从1开始,否则将抛出如下错误:
Exception in thread "main" <openjpa-2.4.2-r422266:1777108 nonfatal user error> org.apache.openjpa.persistence.ArgumentException: Parameter 1 in SQL Query "select id, name, age, salary from user where age < ? and salary > ?" is not given a value. The parameters given is "{2=50, 3=4000.0}".
该占位符是传统 ? 占位符的改良版本,仅仅适应JPQL查询。代码如下:
Query query = em.createQuery( "select t from User t" + " where t.age < ?1 and t.salary > ?2", User.class); query.setParameter(1, 50); query.setParameter(2, 4000.0f); List<User> list = query.getResultList(); for(User user : list) { System.out.println(user); }
输出 SQL 日志如下:
SELECT t0.id, t0.age, t0.name, t0.salary FROM User t0 WHERE (t0.age < ? AND t0.salary > ?) [params=?, ?] User{id=3, name='用户-2', age=44, salary=5855.36} User{id=4, name='用户-3', age=10, salary=4105.1895} User{id=10, name='用户-9', age=26, salary=6327.472}
注意,setParameter() 方法的参数一取值从1开始;如果不是,则抛出如下错误信息:
Exception in thread "main" <openjpa-2.4.2-r422266:1777108 nonfatal user error> org.apache.openjpa.persistence.ArgumentException: Query "select t from User t where t.age < ?2 and t.salary > ?3" did not contain positional parameter 1. JPQL positional parameters must start at 1. Detected parameters "[2, 3]".
变量名形式的占位符是推荐使用的占位符,便于开发者查看,特别是参数多的时候,?n 占位符还需要自己逐个进行比对,不是很方便。注意,该占位符仅仅适应JPQL查询。代码如下:
Query query = em.createQuery( "select t from User t" + " where t.age < :age and t.salary > :salary", User.class); query.setParameter("age", 50); query.setParameter("salary", 4000.0f); List<User> list = query.getResultList(); for(User user : list) { System.out.println(user); }
输出 SQL 日志如下:
SELECT t0.id, t0.age, t0.name, t0.salary FROM User t0 WHERE (t0.age < ? AND t0.salary > ?) [params=?, ?] User{id=3, name='用户-2', age=44, salary=5855.36} User{id=4, name='用户-3', age=10, salary=4105.1895} User{id=10, name='用户-9', age=26, salary=6327.472}
注意,如果将该种占位符应用到本地 SQL 查询,会抛出如下错误信息:
Exception in thread "main" java.lang.IllegalArgumentException: Named parameter "age" is used in native SQL query "select id, name, age, salary from user where age < :age and salary > :salary". But named parameter is not allowed in native SQL query, use integer parameter ?1, ?2 etc.