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 创建本机查询与持久化和注入的主要内容,如果未能解决你的问题,请参考以下文章

通过 JPA EntityManager 执行创建表查询

JPA合并与持久化[重复]

使用Spring时如何注入多个JPA EntityManager(持久化单元)

错误持久化实体列表 - java.lang.IllegalStateException:不允许在共享EntityManager上创建事务

hibernate entitymanager的理解

JPA 和 Play 框架:EntityManager 没有名为更新的持久性提供程序