EntityManager 创建本机查询与持久化和注入
Posted
技术标签:
【中文标题】EntityManager 创建本机查询与持久化和注入【英文标题】:EntityManager create native query vs persist and injections 【发布时间】:2014-02-08 00:45:58 【问题描述】:createNativeQuery()
是否可以安全地防止 SQL 注入:
@ManagedBean
@ViewScoped
public class UserController
@PersistenceContext
private EntityManager em;
public User register(User u)
Query query = em.createNativeQuery("SELECT r1_register(?,?,?,?,?,?,?)");
short i = 0;
query.setParameter(++i, u.getUsername());
query.setParameter(++i, u.getPassword());
query.setParameter(++i, u.getName());
query.setParameter(++i, u.getSurname());
query.setParameter(++i, u.getEmail());
query.setParameter(++i, u.getBirthdate());
query.setParameter(++i, u.getPhoneNumber());
int id = (int) query.getSingleResult();
if (id != 0) u.setIduser(id);
return u;
r1_register
是一个存储函数,它执行 INSERT 并返回新插入用户的 id。这是否等效:
public User register(User u)
em.persist(u);
// get the last inserted id (user id must be @Generated)
em.flush(); // user id set here
return u;
u
在这两种情况下都由用户填写。最后是默认发起的事务吗?
编辑:例程:
CREATE DEFINER=`root`@`localhost` FUNCTION `r1_register`(username VARCHAR(45),
_password VARCHAR(45),
_name VARCHAR(45),
surname VARCHAR(45),
_email VARCHAR(45),
_birthdate DATE,
phone_number VARCHAR(10) ) RETURNS int(11)
BEGIN
-- Adds a new user.
-- START TRANSACTION; -- Begin a transaction -- NOT ALLOWED
-- http://***.com/questions/16969875/
IF r1_check_unique_username(username)=0 THEN
RETURN 0;
END IF;
INSERT IGNORE INTO `hw1_db`.`users` (`username`, `password`, `name`, `surname`, `email`, `birthdate`, `phone_number`)
VALUES (username, _password, _name, surname, _email, _birthdate, phone_number);
-- see: http://***.com/a/5939840/281545
-- The drawback to this approach is that you cannot go back and use
-- ids wasted because of failed attempts to INSERT IGNORE in the event
-- of a duplicate key. Shouldn't be a problem for us as we check.
-- /Transaction
-- IF ROW_COUNT() > 0 THEN
-- ROW_COUNT() returns the number of rows updated/inserted/deleted
-- COMMIT; -- Finalize the transaction
-- ELSE
-- ROLLBACK; -- Revert all changes made before the transaction began
-- END IF;
RETURN LAST_INSERT_ID();
END
【问题讨论】:
一般情况下,参数化 SQL 构造(例如PreparedStatement
)默认转义特殊字符,这样可以防止普通 SQL 注入
@kolossus: 是的,我问的是 JPA api - 例如 persist(Entity e)
在内部转义它持续存在的实体的字符串字段
【参考方案1】:
这取决于r1_register
实际在做什么。如果它只是保存用户而不是别的,那么它们是等价的,因为这就是 EntityManager#persist
正在做的事情。但是如果 DB 函数正在执行一些安全检查或写入其他表,那么您也需要在 JPA 中实现它。顺便说一句,插入User
并获取ID的代码应该是
public User register(User u)
em.getTransaction().begin();
em.persist(u);
em.getTransaction().commit();
int id = u.getId();
return u;
但如果在调用register
方法后需要该ID,则不必调用EntityManager#flush
,刷新在每个事务结束时执行。
【讨论】:
所以他们都防范sql感染?他们也都发起了交易? Mycode 如所见。还有e.getId();
?没有e变量
对不起,我的意思是u
。好吧,EntityManager
确实可以保护您免受 SQL 注入。不,事务是通过进入EJB
bean 中的方法或通过em.getTransaction().begin();
发起的。
你错了我担心em.persist(u);
有或没有flush 不设置id - System.out.println("USER id: " + u.getIduser());
打印0。我仍然想知道是否可以防止注入 - 我是很惊讶他们没有在文档中提到它。此外,我还不确定是否有交易正在进行 - 你能提供一些链接吗?
如果没有活动事务,EM 甚至不会将您的实体插入数据库。这取决于您在哪个上下文中调用它 - 方法 register
是 EJB bean 的一部分吗?如果是,您已经拥有开箱即用的事务,如果没有,那么您需要自己开始并提交它。
对于 SQLi,您使用 EM 真的很安全,除非您将变量连接到 SQL 查询。以上是关于EntityManager 创建本机查询与持久化和注入的主要内容,如果未能解决你的问题,请参考以下文章
使用Spring时如何注入多个JPA EntityManager(持久化单元)
错误持久化实体列表 - java.lang.IllegalStateException:不允许在共享EntityManager上创建事务