Я просто сделал это на основе некоторого аналогичного кода, который у меня уже был, он работает:
Object.byString = function(o, s) {
s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
s = s.replace(/^\./, ''); // strip a leading dot
var a = s.split('.');
for (var i = 0, n = a.length; i < n; ++i) {
var k = a[i];
if (k in o) {
o = o[k];
} else {
return;
}
}
return o;
}
Использование ::
Object.byString(someObj, 'part3[0].name');
См. рабочую демонстрацию в http://jsfiddle.net/alnitak/hEsys/
ИЗМЕНИТЬ, некоторые заметили, что этот код выдает ошибку, если передается строка, где самые левые индексы не соответствуют к правильно вложенной записи внутри объекта. Это актуальная проблема, но IMHO лучше всего обращается с блоком try / catch
при вызове, вместо того, чтобы эта функция молча возвращать undefined
для недопустимого индекса.
Ищете ответы на эту проблему, я наткнулся на эту ссылку
Кажется, что Hibernate / JPA не может автоматически создать значение для вашего не-id- свойства. Аннотации @GeneratedValue
используются только в сочетании с @Id
для создания автоматических номеров.
Аннотация @GeneratedValue
просто сообщает Hibernate, что база данных сама генерирует это значение.
Решение (или обход), предлагаемое на этом форуме, заключается в создании отдельного объекта с генерируемым идентификатором, что-то вроде этого:
@Entity public class GeneralSequenceNumber { @Id @GeneratedValue(...) private Long number; } @Entity public class MyEntity { @Id .. private Long id; @OneToOne(...) private GeneralSequnceNumber myVal; }
Хотя это старый поток, я хочу поделиться своим решением и, надеюсь, получить от него некоторые отзывы. Будьте предупреждены, что я тестировал это решение только с моей локальной базой данных в каком-то тестовом сценарии JUnit. Таким образом, пока это не является продуктивной функцией.
Я решил эту проблему для себя, введя пользовательскую аннотацию, называемую Sequence без свойства.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Sequence
{
}
Используя эту аннотацию, я отметил свои сущности.
public class Area extends BaseEntity implements ClientAware, IssuerAware
{
@Column(name = "areaNumber", updatable = false)
@Sequence
private Integer areaNumber;
....
}
Чтобы сохранить базу данных независимо от нее Я ввел объект под названием SequenceNumber, который содержит текущее значение последовательности и размер приращения. Я выбрал className как уникальный ключ, поэтому каждый класс сущности получит свою собственную последовательность.
@Entity
@Table(name = "SequenceNumber", uniqueConstraints = { @UniqueConstraint(columnNames = { "className" }) })
public class SequenceNumber
{
@Id
@Column(name = "className", updatable = false)
private String className;
@Column(name = "nextValue")
private Integer nextValue = 1;
@Column(name = "incrementValue")
private Integer incrementValue = 10;
... some getters and setters ....
}
Последний шаг и самый сложный - это PreInsertListener, который обрабатывает присвоение порядкового номера. Обратите внимание, что я использовал весну в качестве контейнера для бобов.
@Component
public class SequenceListener implements PreInsertEventListener
{
private static final long serialVersionUID = 7946581162328559098L;
private final static Logger log = Logger.getLogger(SequenceListener.class);
@Autowired
private SessionFactoryImplementor sessionFactoryImpl;
private final Map<String, CacheEntry> cache = new HashMap<>();
@PostConstruct
public void selfRegister()
{
// As you might expect, an EventListenerRegistry is the place with which event listeners are registered
// It is a service so we look it up using the service registry
final EventListenerRegistry eventListenerRegistry = sessionFactoryImpl.getServiceRegistry().getService(EventListenerRegistry.class);
// add the listener to the end of the listener chain
eventListenerRegistry.appendListeners(EventType.PRE_INSERT, this);
}
@Override
public boolean onPreInsert(PreInsertEvent p_event)
{
updateSequenceValue(p_event.getEntity(), p_event.getState(), p_event.getPersister().getPropertyNames());
return false;
}
private void updateSequenceValue(Object p_entity, Object[] p_state, String[] p_propertyNames)
{
try
{
List<Field> fields = ReflectUtil.getFields(p_entity.getClass(), null, Sequence.class);
if (!fields.isEmpty())
{
if (log.isDebugEnabled())
{
log.debug("Intercepted custom sequence entity.");
}
for (Field field : fields)
{
Integer value = getSequenceNumber(p_entity.getClass().getName());
field.setAccessible(true);
field.set(p_entity, value);
setPropertyState(p_state, p_propertyNames, field.getName(), value);
if (log.isDebugEnabled())
{
LogMF.debug(log, "Set {0} property to {1}.", new Object[] { field, value });
}
}
}
}
catch (Exception e)
{
log.error("Failed to set sequence property.", e);
}
}
private Integer getSequenceNumber(String p_className)
{
synchronized (cache)
{
CacheEntry current = cache.get(p_className);
// not in cache yet => load from database
if ((current == null) || current.isEmpty())
{
boolean insert = false;
StatelessSession session = sessionFactoryImpl.openStatelessSession();
session.beginTransaction();
SequenceNumber sequenceNumber = (SequenceNumber) session.get(SequenceNumber.class, p_className);
// not in database yet => create new sequence
if (sequenceNumber == null)
{
sequenceNumber = new SequenceNumber();
sequenceNumber.setClassName(p_className);
insert = true;
}
current = new CacheEntry(sequenceNumber.getNextValue() + sequenceNumber.getIncrementValue(), sequenceNumber.getNextValue());
cache.put(p_className, current);
sequenceNumber.setNextValue(sequenceNumber.getNextValue() + sequenceNumber.getIncrementValue());
if (insert)
{
session.insert(sequenceNumber);
}
else
{
session.update(sequenceNumber);
}
session.getTransaction().commit();
session.close();
}
return current.next();
}
}
private void setPropertyState(Object[] propertyStates, String[] propertyNames, String propertyName, Object propertyState)
{
for (int i = 0; i < propertyNames.length; i++)
{
if (propertyName.equals(propertyNames[i]))
{
propertyStates[i] = propertyState;
return;
}
}
}
private static class CacheEntry
{
private int current;
private final int limit;
public CacheEntry(final int p_limit, final int p_current)
{
current = p_current;
limit = p_limit;
}
public Integer next()
{
return current++;
}
public boolean isEmpty()
{
return current >= limit;
}
}
}
Как вы можете видеть из приведенного выше кода, слушатель использовал один экземпляр SequenceNumber для класса сущности и резервирует пару порядковых номеров, определяемых incrementValue последовательности SequenceNumber организация. Если он не соответствует порядковым номерам, он загружает объект SequenceNumber для целевого класса и резервирует значения incrementValue для следующих вызовов. Таким образом, мне не нужно запрашивать базу данных каждый раз, когда требуется значение последовательности. Обратите внимание на StatelessSession, который открывается для резервирования следующего набора порядковых номеров. Вы не можете использовать один и тот же сеанс, который в настоящее время сохраняется в целевом объекте, поскольку это приведет к исключению ConcurrentModificationException в EntityPersister.
Надеюсь, это поможет кому-то.
«Я не хочу использовать триггер или любую другую вещь, кроме самого Hibernate, для генерации значения для моего свойства»
. В этом случае, как насчет создания реализации UserType, который генерирует требуемое значение и конфигурирование метаданных для использования этого UserType для сохранения свойства mySequenceVal?
Я был в такой ситуации, как вы (последовательность JPA / Hibernate для поля non @Id), и я закончил создание триггера в моей схеме db, которая добавляет уникальный уникальный номер вставки. Я просто не получил его для работы с JPA / Hibernate
Я нашел эту конкретную заметку в сеансе 9.1.9 Аннотацию GeneratedValue из спецификации JPA: «[43] Портативные приложения не должны использовать аннотацию GeneratedValue для других постоянных полей или свойств». Поэтому я предполагаю, что невозможно автоматически генерировать значение для значений не первичного ключа, по крайней мере, используя просто JPA.
Это не то же самое, что использовать последовательность. При использовании последовательности вы ничего не вставляете и не обновляете. Вы просто извлекаете следующее значение последовательности. Похоже, что hibernate не поддерживает его.
Я установил генерацию UUID (или последовательностей) с помощью Hibernate с использованием аннотации @PrePersist
:
@PrePersist
public void initializeUUID() {
if (uuid == null) {
uuid = UUID.randomUUID().toString();
}
}
Как показано ниже, вот как я заработал:
@Override public Long getNextExternalId() {
BigDecimal seq =
(BigDecimal)((List)em.createNativeQuery("select col_msd_external_id_seq.nextval from dual").getResultList()).get(0);
return seq.longValue();
}
SQLQuery sqlQuery = getSession().createSQLQuery("select NAMED_SEQ.nextval seq from dual"); sqlQuery.addScalar("seq", LongType.INSTANCE); return (Long) sqlQuery.uniqueResult();
– Aaron
20 July 2015 в 10:47
Я знаю, что это очень старый вопрос, но он сначала проявляется в результатах, и jpa сильно изменился со времени вопроса.
Правильный способ сделать это теперь с помощью аннотации @Generated
, Вы можете определить последовательность, установить значение по умолчанию в столбце этой последовательности и затем сопоставить столбец как:
@Generated(GenerationTime.INSERT)
@Column(name = "column_name", insertable = false)
Я обнаружил, что @Column(columnDefinition="serial")
работает идеально, но только для PostgreSQL. Для меня это было идеальным решением, потому что второй объект является «уродливым» вариантом.
columnDefinition=
бит в основном говорит Hiberate не пытаться генерировать определение столбца и вместо этого использовать текст, который вы указали. По сути, ваш DDL для столбца будет буквально просто name + columnDefinition. В этом случае (PostgreSQL) mycolumn serial
является допустимым столбцом в таблице.
– Patrick
23 May 2014 в 19:15
@Column(columnDefinition = "integer auto_increment")
– Richard Kennard
28 September 2014 в 11:38
После частых часов это помогло мне решить мою проблему:
Для Oracle 12c:
ID NUMBER GENERATED as IDENTITY
Для H2:
ID BIGINT GENERATED as auto_increment
Также сделайте:
@Column(insertable = false)
Я работаю в той же ситуации, что и вы и я также не нашли серьезных ответов, если в принципе можно генерировать свойства без id с JPA или нет.
Мое решение - вызвать последовательность с собственным запросом JPA, чтобы установить свойство вручную, прежде чем его перенести.
Это не удовлетворяет, но оно работает как обходной момент на данный момент.
Mario
@GeneratedValue
по полям, которые не являются идентификаторами. Проголосовать за включение в 2.2 java.net/jira/browse/JPA_SPEC-113 – Petar Tahchiev 5 April 2016 в 11:40