我有一个问题,关于在mysql和hibernate中使用存储过程的最佳方法是什么。我使用hibernate 4.0.0.Final 作为我的ORM工具运行mysql版本 5.7.14 。现在在mysql数据库中,我有一个存储过程定义如下:
DROP PROCEDURE IF EXISTS LK_spInsertBaseUser;
DELIMITER $$
CREATE PROCEDURE `LK_spInsertBaseUser`(f_name VARCHAR(255),
l_name VARCHAR(255),
n_name VARCHAR(255),
pwd VARCHAR(255),
OUT user_id INT)
BEGIN
## Declaring exit handler
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
GET DIAGNOSTICS CONDITION 1
@state = RETURNED_SQLSTATE,
@errno = MYSQL_ERRNO,
@message = MESSAGE_TEXT;
SET @full_error = CONCAT('ERROR ', @errno, ' (', @state, '): ', @message);
SELECT @full_error;
ROLLBACK;
END;
START TRANSACTION;
IF NOT EXISTS(SELECT first_name
FROM base_user
WHERE first_name = f_name AND last_name = l_name AND nick_name = n_name)
THEN
INSERT INTO base_user (first_name, last_name, nick_name, password)
VALUES (f_name, l_name, n_name, pwd);
SET user_id = LAST_INSERT_ID();
SELECT @user_id AS userId;
ELSE
SET @exiting_user = CONCAT('Base user already exists');
SELECT @exiting_user;
ROLLBACK;
END IF;
END $$
DELIMITER ;
正如我们从上面的过程中看到的那样,如果插入有效,新记录的id将存储在OUT参数 user_id 中,我们也会进行选择。但是,如果出现错误,我会打印出错误。现在,这是问题的核心。尝试通过hibernate执行存储过程时遇到了一些小问题。我终于提出了一个解决方案,但我不相信这是正确的解决方案。让我来看看我经历过的各种尝试。
尝试1 : 我决定对我的BaseUser实体使用 @NamedNativeQueries 注释(注意:base_user sql表映射到BaseUser pojo实体)。以下是代码段:
@SqlResultSetMapping(name="insertUserResult", columns = { @ColumnResult(name = "userId")})
@NamedNativeQueries({
@NamedNativeQuery(
name = "spInsertBaseUser",
query = "CALL LK_spInsertBaseUser(:firstName, :lastName, :nickName, :password, @user_id)",
resultSetMapping = "insertUserResult"
)
})
现在,在Dao类中,我创建了一个通过会话对象调用命名查询的方法,如下所示:
Query query = getSession().getNamedQuery("spInsertBaseUser")
.setParameter("firstName", user.getFirstName())
.setParameter("lastName", user.getLastName())
.setParameter("nickName", user.getNickName())
.setParameter("password", user.getEncodedPassword());
Object data = query.list();
System.out.println(data);
现在这部分工作了。它将数据插入数据库,但数据对象为空。似乎没有设置甚至检索out参数。然后我决定使用不同的方法并使用CallableStatement对象。以下是代码:
尝试2:
getSession().doWork((Connection connection) -> {
CallableStatement statement = connection.prepareCall("{call LK_spInsertBaseUser(?, ? , ?, ?, ?)}");
statement.setString(1, user.getFirstName());
statement.setString(2, user.getLastName());
statement.setString(3, user.getNickName());
statement.setString(4, user.getEncodedPassword());
statement.registerOutParameter(5, Types.INTEGER);
statement.executeUpdate();
System.out.println(statement.getInt(5));
});
这是有效的,而且相当快,但我已经读过,prepareCall的实例化是昂贵的,所以我想真正的问题是,这个解决方案是可接受的标准还是我应该继续在任务中找出NamedNativeQuery方法 更好的效果 ?