目的:为了生成一个不重复的主键,主键生成通过客户端程序生成。
规则主要是根据机器的网络接口信息、线程信息、时间和随机数生成一个不会重复的主键。
详细类如下,改了下名字,根据自己理解加了些注释
最后输出的结果是,前四位是时间标记,然后三位是机器标记,然后1位是进程标记,然后一位是classloader标记,最后三位是自增数字。
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
时间标志 | 机器标志 | 进程标志 | 类加载器标志 | 自增 |
此对象取出后可以使用org.bson.types下的ObjectId对象,取出相应的时间等内容。
/** * Copyright (C) 2008 10gen Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import java.net.*; import java.nio.*; import java.util.*; import java.util.concurrent.atomic.*; import java.util.logging.*; /** * 全局对象唯一标示符 * A globally unique identifier for objects. * <p>Consists of 12 bytes, divided as follows: * <blockquote><pre> * <table border="1"> * <tr><td>0</td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td> * <td>7</td><td>8</td><td>9</td><td>10</td><td>11</td></tr> * <tr><td colspan="4">time</td><td colspan="3">machine</td> * <td colspan="2">pid</td><td colspan="3">inc</td></tr> * </table> * </pre></blockquote> * * @dochub CreateGuIds */ public class CreateGuId implements Comparable<CreateGuId> , java.io.Serializable { private static final long serialVersionUID = -4415279469780082174L; static final Logger LOGGER = Logger.getLogger( "com.easy.todo.util" ); public static void main(String args[]){ System.out.println(CreateGuId.get()); } /** 获取一个新的对象id * Gets a new object id. * @return the new id */ public static CreateGuId get(){ return new CreateGuId(); } /** Checks if a string could be an <code>CreateGuId</code>. * @return whether the string could be an object id */ public static boolean isValid( String s ){ if ( s == null ) return false; final int len = s.length(); if ( len != 24 ) return false; for ( int i=0; i<len; i++ ){ char c = s.charAt( i ); if ( c >= '0' && c <= '9' ) continue; if ( c >= 'a' && c <= 'f' ) continue; if ( c >= 'A' && c <= 'F' ) continue; return false; } return true; } /** Turn an object into an <code>CreateGuId</code>, if possible. * Strings will be converted into <code>CreateGuId</code>s, if possible, and <code>CreateGuId</code>s will * be cast and returned. Passing in <code>null</code> returns <code>null</code>. * @param o the object to convert * @return an <code>CreateGuId</code> if it can be massaged, null otherwise */ public static CreateGuId massageToCreateGuId( Object o ){ if ( o == null ) return null; if ( o instanceof CreateGuId ) return (CreateGuId)o; if ( o instanceof String ){ String s = o.toString(); if ( isValid( s ) ) return new CreateGuId( s ); } return null; } public CreateGuId( Date time ){ this(time, _genmachine, _nextInc.getAndIncrement());//原子操作加1 } public CreateGuId( Date time , int inc ){ this( time , _genmachine , inc ); } public CreateGuId( Date time , int machine , int inc ){ _time = (int)(time.getTime() / 1000); //获取时间戳 _machine = machine; _inc = inc; _new = false; } /** Creates a new instance from a string. * @param s the string to convert * @throws IllegalArgumentException if the string is not a valid id */ public CreateGuId( String s ){ this( s , false ); } public CreateGuId( String s , boolean babble ){ if ( ! isValid( s ) ) throw new IllegalArgumentException( "invalid CreateGuId [" + s + "]" ); if ( babble ) s = babbleToMongod( s ); byte b[] = new byte[12]; for ( int i=0; i<b.length; i++ ){ b[i] = (byte)Integer.parseInt( s.substring( i*2 , i*2 + 2) , 16 ); } //将 byte 数组包装到缓冲区中,新的缓冲区将由给定的 byte 数组支持;也就是说,缓冲区修改将导致数组修改,反之亦然 ByteBuffer bb = ByteBuffer.wrap( b ); _time = bb.getInt(); _machine = bb.getInt(); _inc = bb.getInt(); _new = false; } public CreateGuId( byte[] b ){ if ( b.length != 12 ) throw new IllegalArgumentException( "need 12 bytes" ); ByteBuffer bb = ByteBuffer.wrap( b ); _time = bb.getInt(); _machine = bb.getInt(); _inc = bb.getInt(); _new = false; } /** * Creates an CreateGuId * @param time time in seconds * @param machine machine ID * @param inc incremental value */ public CreateGuId( int time , int machine , int inc ){ _time = time; _machine = machine; _inc = inc; _new = false; } /** Create a new object id. */ public CreateGuId(){ _time = (int) (System.currentTimeMillis() / 1000); _machine = _genmachine; _inc = _nextInc.getAndIncrement(); _new = true; } public int hashCode(){ int x = _time; x += ( _machine * 111 ); x += ( _inc * 17 ); return x; } public boolean equals( Object o ){ if ( this == o ) return true; CreateGuId other = massageToCreateGuId( o ); if ( other == null ) return false; return _time == other._time && _machine == other._machine && _inc == other._inc; } public String toStringBabble(){ return babbleToMongod( toStringMongod() ); } public String toStringMongod(){ byte b[] = toByteArray(); StringBuilder buf = new StringBuilder(24); for ( int i=0; i<b.length; i++ ){ int x = b[i] & 0xFF; //以十六进制(基数 16)无符号整数形式返回一个整数参数的字符串表示形式 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 ); // by default BB is big endian like we need bb.putInt( _time ); bb.putInt( _machine ); bb.putInt( _inc ); return b; } static String _pos( String s , int p ){ return s.substring( p * 2 , ( p * 2 ) + 2 ); } public static String babbleToMongod( String b ){ if ( ! isValid( b ) ) throw new IllegalArgumentException( "invalid object id: " + b ); StringBuilder buf = new StringBuilder( 24 ); for ( int i=7; i>=0; i-- ) buf.append( _pos( b , i ) ); for ( int i=11; i>=8; i-- ) buf.append( _pos( b , i ) ); return buf.toString(); } public String toString(){ return toStringMongod(); } int _compareUnsigned( int i , int j ){ long li = 0xFFFFFFFFL; li = i & li; long lj = 0xFFFFFFFFL; lj = j & lj; long diff = li - lj; if (diff < Integer.MIN_VALUE) return Integer.MIN_VALUE; if (diff > Integer.MAX_VALUE) return Integer.MAX_VALUE; return (int) diff; } public int compareTo( CreateGuId id ){ if ( id == null ) return -1; int x = _compareUnsigned( _time , id._time ); if ( x != 0 ) return x; x = _compareUnsigned( _machine , id._machine ); if ( x != 0 ) return x; return _compareUnsigned( _inc , id._inc ); } public int getMachine(){ return _machine; } /** * 获取id的时间,单位为毫秒 Gets the time of this ID, in milliseconds */ public long getTime(){ return _time * 1000L; } /** * 获取id的时间,单位为秒 Gets the time of this ID, in seconds */ public int getTimeSecond(){ return _time; } public int getInc(){ return _inc; } public int _time(){ return _time; } public int _machine(){ return _machine; } public int _inc(){ return _inc; } public boolean isNew(){ return _new; } public void notNew(){ _new = false; } /** * 获取生成的机器id,此id根据机器网络接口信息,进程和类加载器共同来生成 * Gets the generated machine ID, identifying the machine / process / class loader */ public static int getGenMachineId() { return _genmachine; } /** * 获取自增数字的值 * Gets the current value of the auto increment */ public static int getCurrentInc() { return _nextInc.get(); } final int _time; final int _machine; final int _inc; boolean _new; public static int _flip( int x ){ int z = 0; z |= ( ( x << 24 ) & 0xFF000000 ); //向左位移24位,然后与0xFF000000做按位与运算 z |= ( ( x << 8 ) & 0x00FF0000 ); z |= ( ( x >> 8 ) & 0x0000FF00 ); z |= ( ( x >> 24 ) & 0x000000FF ); return z; } private static AtomicInteger _nextInc = new AtomicInteger( (new java.util.Random()).nextInt() ); private static final int _genmachine; static { try { // 根据机器的网络信息创建两个字节的机器标识 // build a 2-byte machine piece based on NICs info int machinePiece; { try { StringBuilder sb = new StringBuilder(); Enumeration<NetworkInterface> e = NetworkInterface.getNetworkInterfaces(); while ( e.hasMoreElements() ){ NetworkInterface ni = e.nextElement(); sb.append( ni.toString() ); } //网络接口信息例如 name:utun0 (utun0)name:vnic1 (vnic1)name:vnic0 (vnic0)name:en1 (en1)name:lo0 (lo0) 总共有五个网卡接口 //int共32位,左移16位,右边补零,最高位为符号位,正负可能变化,java存负数使用的补码例。 // 位移后是负数的反向计算的时候是先减1再位移,再换算成十进制 // 例如如 782745866 <<16 = -1056309248 // 0010 1110 1010 0111 1100 0001 0000 1010<<16=1100 0001 0000 1010 0000 0000 0000 0000 //再反向转换成十进制为0011 1110 1111 0110 0000 0000 0000 0000 machinePiece = sb.toString().hashCode() << 16; } catch (Throwable e) { // exception sometimes happens with IBM JVM, use random LOGGER.log(Level.WARNING, e.getMessage(), e); machinePiece = (new Random().nextInt()) << 16; } LOGGER.fine( "machine piece post: " + Integer.toHexString( machinePiece ) ); } // 添加 2字节的进程标示,他由jvm和类加载器共同决定 //由于静态变量由类加载器决定,所以可能引发冲突 // add a 2 byte process piece. It must represent not only the JVM but the class loader. // Since static var belong to class loader there could be collisions otherwise final int processPiece; { int processId = new java.util.Random().nextInt(); try { //获取进程标识的hashcode,例如2096@Macintosh.local的hashcode processId = java.lang.management.ManagementFactory.getRuntimeMXBean().getName().hashCode(); } catch ( Throwable t ){ } ClassLoader loader = CreateGuId.class.getClassLoader(); //不论ClassLoader有没有复写hashCode方法,都调用原始的hashCode方法。防止不同的loader重写hashcode方法,造成不同的loader的hashcode相同 int loaderId = loader != null ? System.identityHashCode(loader) : 0; StringBuilder sb = new StringBuilder(); sb.append(Integer.toHexString(processId)); //显示一个byte型的单字节十六进制(两位十六进制表示)的编码 sb.append(Integer.toHexString(loaderId)); //10进制10位改变为16进制等于八位 // //572284967 & 65535 = 24615 processPiece = sb.toString().hashCode() & 0xFFFF; //16位16进制的数取hashcode,再与0xFFFF与操作 LOGGER.fine( "process piece: " + Integer.toHexString( processPiece ) ); } // -1056284633 = -1056309248 | 24615 _genmachine = machinePiece | processPiece; LOGGER.fine( "machine : " + Integer.toHexString( _genmachine ) ); } catch ( Exception e ){ throw new RuntimeException( e ); } } }
相关推荐
MongoDB底层类生成器是一个辅助工具,它根据主键名称自动生成DBHelper类,帮助开发者更便捷地操作数据库,尤其适合对MongoDB初学者。 在关系型数据库中,我们通常会使用ORM(对象关系映射)框架来简化数据操作,而...
在实际应用中,根据业务需求,还可以考虑使用更复杂的数据结构或者算法来生成有序ID,例如Snowflake算法、MongoDB的ObjectID等。这些算法和结构可以提供更高的并发性能和扩展性。 总结来说,"自动生成主键uuid.zip...
在实际应用中,开发者需要根据项目需求和系统环境来评估是否采用Snowflake算法,或者考虑其他分布式ID生成策略,如Twitter的另一算法“V4 UUID”、MongoDB的ObjectID等。同时,提供的压缩包文件可能包含了项目的配置...
4. **Repository接口**: Spring Data MongoDB 支持定义Repository接口,通过接口的方法名自动生成对应的数据库操作。例如,定义一个`UserRepository`: ```java public interface UserRepository extends Mongo...
这里的`MongoRepository`是SpringData MongoDB提供的基础接口,`User`是我们的实体类,`String`是主键类型。`findByUsername`方法是自定义查询,SpringData会自动将其转换为MongoDB的查询语句。 接着,我们来看实体...
每个字段可以使用`@Id`注解来指定主键,或者让MongoDB自动生成。 3. **Repository接口**: Spring Data MongoDB提供了一个通用的Repository接口,如`MongoRepository`,它包含了常见的CRUD操作。我们可以定义自己的...
ObjectId介绍在MongoDB中,文档(document)在集合(collection)中的存储需要一个唯一的_id字段作为主键。这个_id默认使用ObjectId来定义,因为ObjectId定义的足够短小,并尽最大可能的保持唯一性,同时能被快速的...
6. **聚合框架**:Spring Data MongoDB提供了聚合框架,可以方便地执行MongoDB的聚合操作,用于数据分析和报表生成。 7. **地理空间支持**:MongoDB支持地理空间索引和查询,Spring Data MongoDB也提供了相应的API...
使用MyBatisPlus的方式,优雅的操作MongoDB。无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑。损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作。强大的 CRUD 操作:...
4. **主键(_id)**:每个文档都有一个_id字段作为唯一标识符,默认情况下MongoDB会自动生成ObjectId类型的_id字段。 #### 三、MongoDB安装与配置 - **安装步骤**: - 下载MongoDB安装包。 - 解压并安装服务。 ...
只需要定义一个接口,继承自MongoRepository,并提供泛型参数(对应MongoDB集合的实体类型和ID类型),Spring会自动为你生成实现,包括CRUD操作和自定义查询方法。 ```java public interface UserRepository ...
Spring自动为这些接口生成实现,极大地简化了数据访问层的开发工作。 4. **实体类和映射**: 在Spring集成MongoDB的过程中,我们需要定义Java对象(实体类)来表示MongoDB中的文档。这些实体类通常使用`@Document`...
1. **Repository接口**:Spring Data MongoDB通过Repository接口提供了一种声明式的方式来定义数据库操作。例如,你可以定义一个继承自`MongoRepository`的接口,其中包含CRUD(创建、读取、更新、删除)操作的方法...
4. **主键**(_id):MongoDB为每个文档自动生成一个唯一的_id字段作为主键。 #### 四、MongoDB的安装与配置 1. **下载与安装**:可以从MongoDB官网下载适合不同操作系统的版本,按照指引完成安装过程。 2. **启动...
3. **创建模型类**:为了与 MongoDB 存储数据进行交互,你需要定义一个 Java 类作为文档模型,使用注解如 `@Document` 来标记这个类为 MongoDB 文档,`@Id` 来标识主键。 ```java @Document(collection = "users...
4. **Repository接口**:通过定义MongoDB Repository接口,你可以声明查询方法,Spring Data MongoDB会自动为这些方法生成实现。例如,你可以创建一个`UserRepository`,并声明一个`findAll()`方法来获取所有用户。 ...
3. **MongoDB实体映射**:使用`@Document`注解定义MongoDB的集合,`@Id`标注主键,其他的字段可以使用`@Field`进行映射。 4. **Repository接口**:Spring Data MongoDB允许我们定义基于CRUD操作的Repository接口,...
通过这样的方式,Spring Data MongoDB大大简化了MongoDB在Java应用中的使用。它提供了自动化的数据访问功能,使得开发人员可以更专注于业务逻辑,而不是底层数据访问细节。同时,由于Spring框架的灵活性和扩展性,...
与关系数据库不同,MongoDB默认在`_id`字段上创建索引,`_id`作为主键,自动为每个文档生成。 6. **主键(Primary Key)**:在MongoDB中,每个文档都自动有一个`_id`字段作为主键,这个字段通常是全局唯一的...