যদিও এটি একটি পুরানো থ্রেড আমি আমার সমাধানটি ভাগ করতে চাই এবং আশা করি এটি সম্পর্কে কিছু প্রতিক্রিয়া জানাতে পারি। সতর্কতা অবলম্বন করুন যে আমি এই সমাধানটি কয়েকটি স্থানীয় ইউনাইট টেস্টকেসে আমার স্থানীয় ডাটাবেসের সাথে পরীক্ষা করেছি। সুতরাং এটি এখন পর্যন্ত কোনও উত্পাদনশীল বৈশিষ্ট্য নয়।
আমি সম্পত্তিটি ছাড়াই সিকোয়েন্স নামে একটি কাস্টম টীকা প্রবর্তন করে আমার জন্য এই সমস্যাটি সমাধান করেছি। এটি ক্ষেত্রগুলির জন্য কেবল একটি চিহ্নিতকারী যা একটি বর্ধিত ক্রম থেকে একটি মান নির্ধারণ করা উচিত।
@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;
....
}
জিনিসগুলি ডাটাবেসকে স্বাধীন রাখতে আমি সিকোয়েন্স নাম্বার নামে একটি সত্তা প্রবর্তন করি যা ক্রম বর্তমান মান এবং বর্ধিত আকার ধারণ করে। আমি ক্লাসের নামটিকে অনন্য কী হিসাবে বেছে নিয়েছি যাতে প্রতিটি সত্তা শ্রেণি তার নিজস্ব ক্রম পেতে পারে।
@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;
}
}
}
আপনি উপরের কোড থেকে দেখতে পাচ্ছেন শ্রোতা সত্তা শ্রেণি প্রতি একটি সিকোয়েন্স নাম্বার উদাহরণ ব্যবহার করেছেন এবং সিকোয়েন্সবার সত্ত্বার ইনক্রিমেন্টভ্যালু দ্বারা সংজ্ঞায়িত কয়েকটি ক্রম সংখ্যা সংরক্ষণ করেছেন। যদি এটি সিকোয়েন্স সংখ্যার বাইরে চলে যায় তবে এটি লক্ষ্য শ্রেণীর জন্য সিকোয়েন্স নাম্বার সত্তা লোড করে এবং পরবর্তী কলগুলির জন্য ইনক্রিমেন্টভ্যালু মান সংরক্ষণ করে। এইভাবে আমাকে প্রত্যেকবার সিক্যুয়েন্স ভ্যালু প্রয়োজন হলে ডাটাবেসটি জিজ্ঞাসা করতে হবে না। স্টেটলেসসেশনটি নোট করুন যা সিক্যুয়েন্স সংখ্যার পরবর্তী সেটটি সংরক্ষণের জন্য খোলা হচ্ছে। আপনি একই সেশনটি ব্যবহার করতে পারবেন না লক্ষ্য সত্তা বর্তমানে বজায় রয়েছে কারণ এটি সত্তাপরিস্টারগুলিতে একটি সাম্প্রতিককালে রূপান্তরিত হতে পারে।
আশা করি এটি কাউকে সাহায্য করবে।