Sequence Generator in Java for Unique Id

后端 未结 3 1983
面向向阳花
面向向阳花 2020-12-19 01:59

I am planning to write a sequence generator which will be used in my REST resource implementation class during post to generate unique id. Since every post request is handl

相关标签:
3条回答
  • 2020-12-19 02:30

    You can take advantage of java.util.prefs.Preferences to persist the current state of your sequence generator on the disk and use it again later.

    (also, you may want to use several sequence generators)

    i.e.

    import java.lang.ref.SoftReference;
    import java.util.Map;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.atomic.AtomicBoolean;
    import java.util.concurrent.atomic.AtomicLong;
    import java.util.prefs.Preferences;
    
    public final class SequenceGenerator {
    
        private static final Preferences PREFS = Preferences.userNodeForPackage(SequenceGenerator.class);
        private static final AtomicLong SEQ_ID = new AtomicLong(Integer.parseInt(PREFS.get("seq_id", "1")));
        private static final Map<Long, SoftReference<SequenceGenerator>> GENERATORS = new ConcurrentHashMap<>();
        private static final SequenceGenerator DEF_GENERATOR = new SequenceGenerator(0L, Long.parseLong(PREFS.get("seq_0", "1")));
    
        static {
            Runtime.getRuntime().addShutdownHook(new Thread(() -> {
                GENERATORS.values().stream()
                        .map(SoftReference::get)
                        .filter(seq -> seq != null && seq.isPersistOnExit())
                        .forEach(SequenceGenerator::persist);
                if (DEF_GENERATOR.isPersistOnExit()) {
                    DEF_GENERATOR.persist();
                }
                PREFS.put("seq_id", SEQ_ID.toString());
            }));
        }
    
        private final long sequenceId;
        private final AtomicLong counter;
        private final AtomicBoolean persistOnExit = new AtomicBoolean();
    
        private SequenceGenerator(long sequenceId, long initialValue) {
            this.sequenceId = sequenceId;
            counter = new AtomicLong(initialValue);
        }
    
        public long nextId() {
            return counter.getAndIncrement();
        }
    
        public long currentId() {
            return counter.get();
        }
    
        public long getSequenceId() {
            return sequenceId;
        }
    
        public boolean isPersistOnExit() {
            return persistOnExit.get();
        }
    
        public void setPersistOnExit(boolean persistOnExit) {
            this.persistOnExit.set(persistOnExit);
        }
    
        public void persist() {
            PREFS.put("seq_" + sequenceId, counter.toString());
        }
    
        @Override
        protected void finalize() throws Throwable {
            super.finalize();
            GENERATORS.remove(sequenceId);
            if (persistOnExit.get()) {
                persist();
            }
        }
    
        @Override
        public int hashCode() {
            return Long.hashCode(sequenceId);
        }
    
        @Override
        public boolean equals(Object obj) {
            return obj == this || obj != null && obj instanceof SequenceGenerator && sequenceId == ((SequenceGenerator) obj).sequenceId;
        }
    
        @Override
        public String toString() {
            return "{" +
                    "counter=" + counter +
                    ", seq=" + sequenceId +
                    '}';
        }
    
        public static SequenceGenerator getDefault() {
            return DEF_GENERATOR;
        }
    
        public static SequenceGenerator get(long sequenceId) {
            if (sequenceId < 0) {
                throw new IllegalArgumentException("(sequenceId = " + sequenceId + ") < 0");
            }
            if (sequenceId == 0) {
                return DEF_GENERATOR;
            }
            SoftReference<SequenceGenerator> r = GENERATORS.computeIfAbsent(sequenceId, sid -> {
                try {
                    return new SoftReference<>(new SequenceGenerator(sid, Long.parseLong(PREFS.get("seq_" + sid, null))));
                } catch (Throwable t) {
                    return null;
                }
            });
            return r == null ? null : r.get();
        }
    
        public static SequenceGenerator create() {
            return create(1);
        }
    
        public static SequenceGenerator create(long initialValue) {
            long sequenceId = SEQ_ID.getAndIncrement();
            SequenceGenerator seq = new SequenceGenerator(sequenceId, Long.parseLong(PREFS.get("seq_" + sequenceId, "" + initialValue)));
            GENERATORS.put(sequenceId, new SoftReference<>(seq));
            return seq;
        }
    
    }
    
    0 讨论(0)
  • 2020-12-19 02:33

    It will work, however AtomicInteger is an built in type that is perfect for your use case.

    AtomicInteger seq = new AtomicInteger();
    int nextVal = seq.incrementAndGet();
    
    0 讨论(0)
  • 2020-12-19 02:46

    If you are open to using String for IDs, instead of int, you might want to look into using UUID (Universally Unique Identifier). Very easy to use and as the name implies, they are unique. Here is an example of how to generate one:

    // the value of uuid will be something like '03c9a439-fba6-41e1-a18a-4c542c12e6a8'
    String uuid = java.util.UUID.randomUUID().toString()
    

    A UUID also provides better security than int because with integers you can guess the next request ID by simply adding 1 to your request ID, but UUIDs are not sequential and chances of anyone guessing anyone else's request ID are pretty slim.

    0 讨论(0)
提交回复
热议问题