я отвечаю на этот вопрос в надежде, что это поможет вам непосредственно, но и, если нет, то, что другие, которые имели этот дракон Joust с натыкается ответ здесь на SO. Это настоящая doozie по-своему, и вы думаете, что будет очень четкое обсуждение того, как обращаться с такими вещами в каждой основной инфо-палубе Hibernate, но, к сожалению, здесь и там есть только разбросанные биты. намекает на этом пути. После битвы в течение пары дней, чтобы собрать вместе исправление, я нашел, что сработало для меня.
Во-первых, я должен использовать файлы .hbm.xml и сопоставления, а не аннотации. Это связано с отсутствием объявленных ПК в используемых таблицах, а для них ПК (суррогатные ключи, по существу), является фиксированным (это не входит в мой контроль). Аннотации кажутся «счастливыми», если на таблицах объявлены PK. Если нет, вы должны использовать класс идентификации для каждого класса таблицы. (Например: у вас есть таблица: «MyTable». Если вы, например, используете Hib. Reverse Engineering Tools в Eclipse, он будет генерировать для вас два исходных файла: MyTable.java и MyTableId.java (и абстрактные классы тоже, если вы попросите их). MyTableId.java - это тот, который будет содержать фактические значения.)
Я работаю с Hibernate 3.3. Пока мы еще не до 4.x.
И, наконец, используемая база данных - это Oracle. Моя задача состояла в том, чтобы запустить SP, который принял один параметр и не возвратил никаких записей. Это система SP, используемая для привязки произвольного строкового значения к текущему сеансу связи («USERENV»), связанного с ключевым значением «CLIENT_IDENTIFIER». Предполагается, что разработчик должен назначить сеансу идентификационную строку пользователя приложения (например, зарегистрированный идентификатор пользователя), который затем может быть извлечен в триггере или в SP на стороне базы данных. Это извлечение является легкой частью; он получает Hibernate, чтобы позволить вам запустить этот SP, который является тяжелой частью.
Раньше бывало, что вы могли бы захватить базовое соединение Oracle и запустить вызов SP по нему без особых фанфаров. Это выглядело так:
String userid = "<something from someplace>";
Session session = getSession(); // whatever way you get your Hib. session.
/* 'WSCallHelper' as used below is a helper class found in the IBM Websphere API
programmer library. In some other context, a programmer would use a different
means to isolate the Oracle-native connection. */
OracleConnection oracleconnection =
(OracleConnection) WSCallHelper.getNativeConnection(session.connection());
CallableStatement st = oracleconnection.
prepareCall("{call DBMS_SESSION.SET_IDENTIFIER(:userid)}");
st.setString("userid", userid);
st.execute();
проблема сейчас: session.connection() является устаревшим в 3.3, и в самой последней версии 4.x, вы не найдете его на всех в спящем «Session» класса Javadocs.
Это означает, что вам нужно, если вы планируете модернизировать (??) ваша версия Hibernate некоторое время до 4.x и будет содержать такой код, он перестанет работать. Если вы планируете написать что-то новое и не знаете, будете ли вы обновлять или нет, лучше безопасно, чем сожалеть. (Вы не хотите звонить в 3:00 утра, не так ли?)
Первое, на что я столкнулся, когда искал способ запуска SP на Oracle с использованием собственного объекта SQL Query (SQLQuery) или запрос HQL (объект запроса) состоит в том, что у обоих есть проблема с поддержкой только действия выбора или действия обновления: .list() и .executeUpdate(). Нет простого и простого .execute(), как вы находите в других DAL или в java.sql. SP, который я хотел запустить [DBMS_SESSION.SET_IDENTIFIER (userid)] ничего не возвращает. Кроме того, все усилия, которые я предпринял для простого сеанса Hibernate, вызвали строку {CALL DBMS_SESSION.SET_IDENTIFIER (userid)}. Я попытался futzing с синтаксисом утверждения и т. Д. Нет кубиков.
Наконец, после многократного серфинга я понял, что если Hibernate ожидает, что что-то будет возвращено (любое значение) из SP, оно должно рассматриваться как объект базы данных. Это соответствует попытке запустить даже простой, неквалифицированный оператор select, используя Hibernate; если у вас нет аннотированных файлов Java, сопоставляющих данные с таблицами или файлами .hbm.xml, аналогично, Hibernate просто не будет сотрудничать - и это идея. Поэтому мне пришлось придумать способ представления отношения SP к базе данных, даже несмотря на то, что нет таблицы, которая сопоставляется с ней. Дракона нужно было обмануть.
Шаг 1: Создайте файл .hbm.xml для двойки (да, псевдо-таблица в Oracle), но только если вы не используете аннотации. Это должно выглядеть очень похоже на это, модифицированный для вашей структуры пакета, имя запроса, и фактическое SP вы хотите запустить, по желанию/необходимости:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.company.hibernate.dataaccess.model.Dual" table="DUAL">
<id name="dummy" type="java.lang.String">
<column name="DUMMY" />
<generator class="identity" />
</id>
</class>
<sql-query name="callDdbmsSessionSetIdentifier">
<return alias="dummy" class="com.company.hibernate.dataaccess.model.Dual"/>
<![CDATA[CALL DBMS_SESSION.SET_IDENTIFIER(:userid)]]>
</sql-query>
</hibernate-mapping>
Обратите внимание: вызов DBMS_SESSION.SET_IDENTIFIER находится в Блок CDATA, как и любой другой CALL. (У меня есть Mkyong, чтобы поблагодарить за этот намек: http://www.mkyong.com/hibernate/hibernate-named-query-examples/) Также обратите внимание, что < класс > нет не не обязательно. Это решение не будет работать без него. (Если вы выполните «выбор * из DUAL» во время входа в Oracle, вы получите один столбец с именем «DUMMY» и одну строку обратно с одним полем, имеющим значение «X». Поэтому вам нужно объявить «DUMMY ", как показано на рисунке. И не беспокойтесь о ссылке < generator class =" identity "/ >, так как вы никогда ничего не будете экономить на DUAL.)
Если вы используете аннотации, сделайте аннотацию эквивалент вышесказанного в вашем файле «Dual.java», что вам не нужно, если вы используете аннотации или сопоставления .hbm.xml. Этот файл обсуждается далее. Последним шагом будет добавление правильной ссылки на ваш файл hibernate.cfg.xml.
Шаг 2: Создайте файл "Dual.java" Как было показано выше, для примера, предположим, пакет com.company.hibernate.dataaccess.model:
package com.company.hibernate.dataaccess.model;
public class Dual implements java.io.Serializable {
private String dummy = "";
public void setDummy (String s) {
dummy = s;
}
public String getDummy() {
return dummy;
}
}
Вот и все ! Теперь, если вы используете аннотации, вам придется добавить те, которые подходят.
Update 12/10/13: Пошел к Hib 3.6.3 и теперь можно использовать аннотации OK для этого, поэтому мы угробили .hbm файлы. Двойной.Java-файл теперь в использовании выглядит следующим образом:
package {whatever};
import javax.persistence.*; // Better to name each entity, but using * for brevity
import org.hibernate.annotations.NamedNativeQueries;
import org.hibernate.annotations.NamedNativeQuery;
@NamedNativeQueries({
@NamedNativeQuery(
name = "callDdbmsSessionSetIdentifier",
query = "CALL DBMS_SESSION.SET_IDENTIFIER(:userid)",
resultClass = Dual.class
)
})
@Entity
@Table(name = "DUAL", schema = "SYS")
public class Dual implements java.io.Serializable {
private DualId id;
public Dual() {
}
public Dual(DualId id) {
this.id = id;
}
@EmbeddedId
@AttributeOverrides({
@AttributeOverride(name = "dummy", column = @Column(name = "DUMMY", nullable = false, length = 1)),
})
public DualId getId() {
return this.id;
}
public void setId(DualId id) {
this.id = id;
}
}
В случае, если вы заботитесь, DualId.java выглядит следующим образом:
package {whatever};
import javax.persistence.Column;
import javax.persistence.Embeddable;
@Embeddable
public class DualId implements java.io.Serializable {
private String dummy;
public DualId() {
}
public DualId(String dummy) {
this.dummy = dummy;
}
// Bean compliance only; 'DUMMY' can't be changed in DUAL and
// why you'd care to get it when you know already it's just an "X",
// dunno. But these get.. and set.. methods are needed anyway.
@Column(name = "DUMMY", nullable = false, length = 1)
public String getDummy() {
return this.dummy;
}
public void setDummy(String dummy) {
}
public boolean equals(Object other) {
if ((this == other))
return true;
if ((other == null))
return false;
if (!(other instanceof DualId))
return false;
DualId castOther = (DualId) other;
return ((this.getDummy() == castOther.getDummy()) || (this.getDummy() != null && castOther.getDummy() != null && this
.getDummy().equals(castOther.getDummy())));
}
public int hashCode() {
int result = 17;
result = 37 * result + (getDummy() == null ? 0 : this.getDummy().hashCode());
return result;
}
}
Шаг 3: Обновление файла hibernate.cfg.xml
Добавьте эту строку в свой список картографических записей ресурсов, если используете файлы .hbm.xml, корректирующие дорожки или пакеты, чтобы уточнить:
<mapping resource="com/company/hibernate/hbm/Dual.hbm.xml" />
При использовании аннотаций, добавьте следующую строку:
<mapping class="com.company.hibernate.dataaccess.model.Dual" />
Почти сделано! Все спасено? Отлично.
В любой исходный файл у вас есть, что вы хотите назвать СП с, код будет, по крайней мере, в моем случае, выглядит следующим образом:
Session session = getSession(); // somehow...
String userid = "<got this someplace>";
Query query = session.getNamedQuery("callDdbmsSessionSetIdentifier").
setParameter("userid", userid);
try {
query.list();
} catch (Exception e) { }
ОК, что происходит? Обратите внимание, что именованный запрос является «callDdbmsSessionSetIdentifier». Это то, что я использовал для обозначения фактического запроса, определенного в файле .hbm.xml (см. Выше, посмотрите на код < sql-query name = "callDdbmsSessionSetIdentifier" >). Теперь обратите внимание, что я уловил исключение, вызванное вызовом query.list() и потребляющим его. Обычно это было бы огромным нет-нет, не так ли? Ну, вы можете сообщить об этом, если хотите. Вы можете сообщить об этом, просто зарегистрировав свое сообщение, а не всю трассу, если вы хотите, чтобы ваши журналы не заполнялись множеством нежелательных сообщений. Исключение вы получите будет такие вещи, как:
(дата-время) - JDBCException E org.hibernate.util.JDBCExceptionReporter logExceptions Невозможно выполнить выборку на PLSQL заявление: следующий ... (дата- время) - SystemErr R org.hibernate.exception.GenericJDBCException: не удалось выполнить запрос в org.hibernate.exception.SQLStateConverter.handledNonSpecificException (SQLStateConverter.java:82) ... Вызванный: java.sql.SQLException: Невозможно выполнить выборку в инструкции PLSQL: next at oracle.jdbc.driver.OracleResultSetImpl.next (OracleResultSetImpl.ja va: 240) ...
Обратите внимание на общую тему: Hibernate не может получить запись любого вида. Это не тот вид исключения, о котором вам нужно беспокоиться, если все, что вы делаете, это выполнение SP и не хотите извлекать записи из него.
Как всегда, проверьте и проверьте еще несколько ... убедитесь, что он действительно делает то, что вы хотите. Независимо от того, хотите ли вы регистрировать какие-либо ошибки или просто использовать ошибки, зависит от вас.
НАКОНЕЦ, что бы вы сделали, если, как я, вы хотите использовать DBMS_SESSION.SET_IDENTIFIER() хранимую процедуру в Oracle, так что вы могли бы получить некоторые вошедшие в профиле ID пользователя в базе данных на стороне триггер или SP?Вот база данных на стороне PL/SQL для этого, достаточно просто:
USERNAME VARCHAR2(50) := NULL;
...
select SYS_CONTEXT('USERENV', 'CLIENT_IDENTIFIER')
INTO USERNAME
from DUAL;
...
В моем конкретном случае, я использую это в качестве триггера, который служит для регистрации любых изменений в определенную таблицу для целей аудита. Но изменения могут происходить из любого места: пользователь настольного SQL * Plus, приложение, которое может или не может использовать процедуру DBMS_SESSION.SET_IDENTIFIER() и т. Д., И, следовательно, не может быть установлено значение CLIENT_IDENTIFIER в USERENV для любого заданного соединение сеанса. Если это так, USERNAME вернется как null. Так что у меня есть этот дополнительный блок сразу же после того, как один выше, который получает идентификатор соединения в случае CLIENT_IDENTIFIER равна нулю:
IF USERNAME IS NULL THEN
SELECT USER INTO USERNAME FROM DUAL;
END IF;
поэтому у меня есть что-то, чтобы поместить в поле идентификатора в таблице аудита.
Выполнено. Надеюсь, это поможет кому-то. Не стесняйтесь комментировать.
Для этого вам необходимо использовать собственный запрос, насколько я знаю. –
Я использовал createNativeQuery, а также не удалось выполнить «execute do_build» (мне также пришлось удалить Boolean.class) – mathematician