हालाँकि यह एक पुराना धागा है जो मैं अपने समाधान को साझा करना चाहता हूं और उम्मीद है कि इस पर कुछ प्रतिक्रिया मिलेगी। चेतावनी दी है कि मैं केवल कुछ 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 नाम की एक इकाई शुरू की, जो अनुक्रम वर्तमान मूल्य और वृद्धि आकार रखती है। मैंने क्लासनेम को अद्वितीय कुंजी के रूप में चुना ताकि प्रत्येक इकाई वर्ग वाईएल को अपना अनुक्रम मिल सके।
@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 इकाई को लोड करता है और अगले कॉल के लिए वृद्धिशील मानों को आरक्षित करता है। इस तरह मुझे हर बार अनुक्रम मान की जरूरत है डेटाबेस को क्वेरी करने की आवश्यकता नहीं है। नोट करें कि स्टेटलेस नेशन को अनुक्रम संख्या के अगले सेट को संग्रहीत करने के लिए खोला जा रहा है। आप उसी सत्र का उपयोग नहीं कर सकते हैं जो लक्ष्य इकाई वर्तमान में बनी हुई है क्योंकि इससे EntityPistister में एक समवर्तीमॉडिफिकेशन अपवाद हो जाएगा।
आशा है कि यह किसी की मदद करता है।