java生成不重复的字符串类型ID

本文介绍了一个Java工具类IdGenerator,用于生成24位不重复的字符串ID,通过结合系统时间戳和机器标识实现。它提供get()方法获取ID,并包含了哈希码比较、转化为Mongod格式以及内部实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

此工具类用于生成24位字符串ID,唯一不重复。
直接通过 IdGenerator.get() 获取。


import java.net.NetworkInterface;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Enumeration;
 
/**
 * 生成24位字符串ID
 * 
 */
public class IdGenerator implements Comparable<IdGenerator> {
 
    /**
     * 调用该方法获取24位字符串ID
     * @return
     */
    public static String get() {
        return new IdGenerator().toString();
    }
 
    public IdGenerator() {
        _time = _gentime;
        _machine = _genmachine;
        synchronized (_incLock) {
            _inc = _nextInc++;
        }
        _new = true;
    }
 
    public int hashCode() {
        return _inc;
    }
 
    public String toStringMongod() {
        byte b[] = toByteArray();
 
        StringBuilder buf = new StringBuilder(24);
 
        for (int i = 0; i < b.length; i++) {
            int x = b[i] & 0xFF;
            String s = Integer.toHexString(x);
            if (s.length() == 1)
                buf.append("0");
            buf.append(s);
        }
 
        return buf.toString();
    }
 
    public byte[] toByteArray() {
        byte b[] = new byte[12];
        ByteBuffer bb = ByteBuffer.wrap(b);
        bb.putInt(_inc);
        bb.putInt(_machine);
        bb.putInt(_time);
        reverse(b);
        return b;
    }
 
    static void reverse(byte[] b) {
        for (int i = 0; i < b.length / 2; i++) {
            byte t = b[i];
            b[i] = b[b.length - (i + 1)];
            b[b.length - (i + 1)] = t;
        }
    }
 
    static String _pos(String s, int p) {
        return s.substring(p * 2, (p * 2) + 2);
    }
 
    public String toString() {
        return toStringMongod();
    }
 
    public int compareTo(IdGenerator id) {
        if (id == null)
            return -1;
 
        long xx = id.getTime() - getTime();
        if (xx > 0)
            return -1;
        else if (xx < 0)
            return 1;
 
        int x = id._machine - _machine;
        if (x != 0)
            return -x;
 
        x = id._inc - _inc;
        if (x != 0)
            return -x;
 
        return 0;
    }
 
    public int getMachine() {
        return _machine;
    }
 
    public long getTime() {
        long z = _flip(_time);
        return z * 1000;
    }
 
    public int getInc() {
        return _inc;
    }
 
    final int _time;
    final int _machine;
    final int _inc;
 
    boolean _new;
 
    static int _flip(int x) {
        byte b[] = new byte[4];
        ByteBuffer bb = ByteBuffer.wrap(b);
        bb.order(ByteOrder.LITTLE_ENDIAN);
        bb.putInt(x);
        bb.flip();
        bb.order(ByteOrder.BIG_ENDIAN);
        return bb.getInt();
    }
 
    private static int _nextInc = (new java.util.Random()).nextInt();
    private static final String _incLock = new String("IdGenerator._incLock");
 
    private static int _gentime = _flip((int) (System.currentTimeMillis() / 1000));
 
    static final Thread _timeFixer;
    private static final int _genmachine;
    static {
        try {
            final int machinePiece;
            {
                StringBuilder sb = new StringBuilder();
                Enumeration<NetworkInterface> e = NetworkInterface.getNetworkInterfaces();
                while (e.hasMoreElements()) {
                    NetworkInterface ni = e.nextElement();
                    sb.append(ni.toString());
                }
                machinePiece = sb.toString().hashCode() << 16;
            }
 
            final int processPiece = java.lang.management.ManagementFactory.getRuntimeMXBean().getName().hashCode() & 0xFFFF;
            _genmachine = machinePiece | processPiece;
        } catch (java.io.IOException ioe) {
            throw new RuntimeException(ioe);
        }
 
        _timeFixer = new Thread("IdGenerator-TimeFixer") {
            public void run() {
                while (true) {
                    try {
                        Thread.sleep(499);
                    } catch (InterruptedException e) {
                    }
                    _gentime = _flip((int) (System.currentTimeMillis() / 1000));
                }
            }
        };
        _timeFixer.setDaemon(true);
        _timeFixer.start();
    }
 
}

转载:https://www.cnblogs.com/lr393993507/p/5457437.html

<think>好的,现在用户提到他们的ID都是字符串类型,之前的问题是关于如何获取最近一次插入的自增ID,但当时讨论的是自增整数ID的情况。现在用户的情况同,他们的ID字符串类型的,这意味着传统的自增ID方法可能适用。首先,我需要确认用户的需求是什么。用户可能仍然想获取最近插入的字符串ID,但这类ID通常是自增的,所以之前的方法可能无效。 接下来,我需要考虑用户为什么会使用字符串类型ID。可能的情况包括使用UUID、业务相关的自定义字符串或者其他生成方式。这时候,使用LAST_INSERT_ID()函数可能无法工作,因为该函数依赖于自增的数值型字段。而MAX(id)函数虽然可以获取最大的ID,但如果ID字符串,排序可能是按插入顺序,而是按字符串的字典顺序,这样结果可能准确。 用户的需求可能是在插入一条记录后,立即获取生成字符串ID。这时候需要明确用户是如何生成这些字符串ID的。如果是应用层生成的,比如在代码中生成UUID,那么在插入后可以直接使用该值,无需查询数据库。如果是数据库生成的,比如通过触发器或存储过程,可能需要特定的方法获取。 如果用户的字符串ID是应用层生成的,比如使用UUID()函数,那么在插入时就可以直接获取生成ID。例如,在插入语句中使用UUID()生成ID,然后返回该值。或者在编程语言中生成UUID,插入时作为参数传入,之后直接使用生成字符串。 如果ID是由数据库自动生成的,可能需要使用数据库的特定函数或方法。例如,在MySQL中,使用触发器在插入时生成字符串ID,这时候可能需要通过查询特定表或使用OUTPUT语句(在支持的情况下)来获取生成ID。 另外,用户可能没有明确说明他们的ID生成机制,这时候需要进一步询问或提供几种可能的解决方案。例如,假设用户使用的是应用层生成的UUID,可以直接在插入后获取;如果使用数据库函数生成,可能需要同的方法。 还需要考虑到并发情况下的准确性。例如,当多个用户同时插入数据时,如何确保获取到正确的最近ID。使用应用层生成ID可以在插入时立即获得,避免了数据库查询的延迟和并发问题。而如果依赖数据库生成,可能需要事务控制来确保数据的一致性。 此外,用户可能关心性能和效率。使用MAX(id)在字符串类型ID上可能效率低下,尤其是当表数据量较大时,全表扫描会影响性能。因此,建议用户避免使用这种方法,而是采用应用层生成并记录ID的方式。 最后,需要总结可能的解决方案,并给出最佳实践建议。例如,推荐用户在应用层生成唯一ID,并在插入时直接获取,或者在数据库中使用触发器配合其他方法获取生成字符串ID。同时,提醒用户注意并发和性能问题,确保系统的可靠性和效率。</think>若表中ID字段为字符串类型(非自增数字),获取最新插入记录的方法需要根据具体ID生成规则进行调整。以下是同场景下的解决方案: --- ### 一、应用层生成ID的场景(推荐) **适用情况**:ID由程序生成(如UUID、时间戳+随机数等) #### 操作步骤: 1. **生成ID** 在代码中生成唯一字符串ID(如UUID),无需依赖数据库 ```python import uuid new_id = str(uuid.uuid4()) # 生成UUID ``` 2. **插入时显式指定ID** ```sql INSERT INTO your_table (id, name) VALUES ('{new_id}', '示例数据'); ``` 3. **直接使用生成ID** 无需额外查询,插入时已明确知道ID值 --- ### 二、数据库生成ID的场景 **适用情况**:ID通过数据库函数/触发器生成(如组合时间戳、序列等) #### 方法1:使用`RETURNING`子句(需MySQL 8.0.31+) ```sql /* 创建含触发器生成ID表 */ CREATE TABLE demo_table ( id VARCHAR(36) PRIMARY KEY, content TEXT ); -- 插入数据并返回生成ID INSERT INTO demo_table (content) VALUES ('测试内容') RETURNING id; -- 直接返回插入的ID ``` #### 方法2:通过时间戳字段倒序查询 ```sql /* 前提:表中有记录插入时间的字段 create_time */ SELECT id FROM your_table ORDER BY create_time DESC LIMIT 1; ``` --- ### 三、特殊ID类型处理方案 | ID生成方式 | 获取方法 | 注意事项 | |------------------|-----------------------------|----------------------------| | UUID | 应用层生成后直接使用 | 确保唯一性算法可靠 | | 时间戳+序列号 | 通过时间字段倒序查询 | 高并发时可能重复 | | 哈希值 | 插入时计算并保存 | 需处理哈希冲突 | | 数据库函数生成 | 使用`RETURNING`子句获取 | 需要MySQL 8.0.31+版本支持 | --- ### 四、性能优化建议 1. **添加插入时间字段** ```sql ALTER TABLE your_table ADD create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP; ``` 2. **建立倒序索引** ```sql CREATE INDEX idx_create_time ON your_table (create_time DESC); ``` 3. **限制查询范围** ```sql SELECT id FROM your_table WHERE create_time > NOW() - INTERVAL 1 HOUR ORDER BY create_time DESC LIMIT 1; ``` --- ### 五、同编程语言示例 #### Python(使用psycopg2): ```python import uuid new_id = str(uuid.uuid4()) cursor.execute("INSERT INTO table (id, data) VALUES (%s, %s)", (new_id, "test")) print("新插入ID:", new_id) # 直接使用生成ID ``` #### Java(JDBC): ```java String uuid = UUID.randomUUID().toString(); String sql = "INSERT INTO table (id, data) VALUES (?, ?)"; PreparedStatement pstmt = conn.prepareStatement(sql); pstmt.setString(1, uuid); pstmt.setString(2, "test"); pstmt.executeUpdate(); System.out.println("新插入ID: " + uuid); ``` --- ### 关键区别总结 | 方法 | 优点 | 缺点 | |---------------------|--------------------------|--------------------------| | 应用层生成ID | 无数据库依赖,性能最佳 | 需保证生成算法可靠性 | | RETURNING子句 | 原子操作,准确可靠 | 需要MySQL 8.0.31+版本 | | 时间戳倒序查询 | 兼容所有版本 | 需要额外字段,存在延迟 | 建议优先采用应用层生成唯一ID的方案,既能保证效率又可避免数据库查询带来的性能损耗。若必须使用数据库生成,建议升级到MySQL 8.0+版本并使用`RETURNING`子句。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值