A simple and efficient order ID generator, can help you easily generate readable and beautiful business primary keys. Especially when if you are suffering from complex framework configuration and many distributed coordination services, you can try it.
- Complete Snowflake ID Implementation
- Breaking through the concurrency limit that Snowflake ID can only generate 4095 valid IDs in 1 millisecond.
- Clean configuration interface.
- Option to not depend on any service.
- Support Java17
- Step1. Add this dependency to your
pom.xml
<dependency>
<groupId>ink.organics</groupId>
<artifactId>id-generator</artifactId>
<version>3.0.2</version>
</dependency>
- Step2. Initialize a generator. like in the Spring.
import ink.organics.idgenerator.generator.Generator;
import ink.organics.idgenerator.generator.impl.SnowflakeGenerator;
@Configuration
public class ApplicationConfiguration {
@Bean
public Generator idGenerator() {
return SnowflakeGenerator.build(
"server_1", // Current service identifier
List.of("server_1", "server_2")); // All services identifier.
}
}
- You need to pay attention
- First parameter. You need to ensure that the current service identifier is globally unique in your cluster. More precisely, it is unique among all JVM processes in the cluster.
- Second parameter. You need to ensure that List's elements and the order of the elements are the same in all services, and the maximum size cannot exceed 1023.
- E.g The same code in different processes.
@Configuration
public class ApplicationConfiguration {
@Bean
public Generator idGenerator() {
return SnowflakeGenerator.build(
"server_1",
List.of("server_1", "server_2"));
}
}
@Configuration
public class ApplicationConfiguration {
@Bean
public Generator idGenerator() {
return SnowflakeGenerator.build(
"server_2",
List.of("server_1", "server_2"));
}
}
- Step3. Use it anywhere.
import ink.organics.idgenerator.generator.Generator;
@SpringBootTest
public class SpringTest {
@Autowired
private Generator generator;
@Test
public void test1() {
long id = generator.next();
assertThat(id).isGreaterThan(76976953847971840L);
}
}
- Find a place to initialize.
import ink.organics.idgenerator.IDGeneratorManager;
import ink.organics.idgenerator.decorator.Decorator;
import ink.organics.idgenerator.generator.impl.SnowflakeGenerator;
public class ApplicationConfiguration {
public void initIdGeneratorManager() {
IDGeneratorManager.getInstance()
.init(Decorator.builder() // Build a decorator
.generatorId("generatorId_1") // The decorator need a id
.generator(SnowflakeGenerator.build("server_1", List.of("server_1", "server_2")))
.build());
}
}
- Use it anywhere.
import ink.organics.idgenerator.generator.Generator;
public class DemoTest {
@Test
public void test2() {
long id = IDGenerator.next("generatorId_1"); // Need decorator id
assertThat(id).isGreaterThan(76976953847971840L);
}
}
- Initialize some decorator.
import ink.organics.idgenerator.IDGeneratorManager;
import ink.organics.idgenerator.decorator.Decorator;
import ink.organics.idgenerator.decorator.impl.StringDecoratorRule;
import ink.organics.idgenerator.generator.Generator;
import ink.organics.idgenerator.generator.impl.SnowflakeGenerator;
public class ApplicationConfiguration {
public void initIdGeneratorManager() {
IDGeneratorManager.getInstance().init(
Decorator.builder() // Build a decorator
.generatorId("generatorId_1") // The decorator need a id
.generator(SnowflakeGenerator.build("server_1", List.of("server_1", "server_2")))
.decoratorRule(StringDecoratorRule.builder().prefix("QQQ").autoComplete(true).build()) // Optional. Set some rules
.build(),
Decorator.builder()
.generatorId("generatorId_2")
.generator(SnowflakeGenerator.build("server_1", List.of("server_1", "server_2")))
.decoratorRule(StringDecoratorRule.builder().postfix("WWW").autoComplete(false).build())
.build()
);
}
}
- Use it anywhere.
import ink.organics.idgenerator.IDGenerator;
import ink.organics.idgenerator.generator.Generator;
public class DemoTest {
@Test
public void test2() {
String generatorId_1 = IDGenerator.nextToString("generatorId_1");
assertThat(generatorId_1).startsWith("QQQ");
String generatorId_2 = IDGenerator.nextToString("generatorId_2");
assertThat(generatorId_2).endsWith("WWW");
}
}
- Initialize a generator
@Configuration
public class ApplicationConfiguration {
@Bean
public IDGeneratorManager idGeneratorManager() {
return IDGeneratorManager.getInstance()
.init(Decorator.builder() // Build a decorator
.generatorId("generatorId_1") // The decorator need a id
.generator(SnowflakeGenerator.build("server_1", List.of("server_1", "server_2")))
.decoratorRule(StringDecoratorRule.builder().prefix("QQQ").autoComplete(true).build()) // Optional. Set some rules
.build());
}
}
- Create a Hibernate IdentifierGenerator
import ink.organics.idgenerator.IDGenerator;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.IdentifierGenerator;
import java.io.Serializable;
public class MyGenerator implements IdentifierGenerator {
@Override
public Serializable generate(SharedSessionContractImplementor sharedSessionContractImplementor, Object o) throws HibernateException {
return IDGenerator.nextToString("generatorId_1");
}
}
- Use it to your entity
@Data
@Entity(name = "user_t")
public class User extends BaseEntity {
@Id
@GeneratedValue(generator = "my-generator")
@GenericGenerator(name = "my-generator", strategy = "com.example.MyGenerator")
private String id;
private String name;
private Integer age;
private String email;
}
- Test it.
@SpringBootTest
public class SpringDemoTest {
@Autowired
private UserRepository userRepository;
@Test
public void test3() {
User user = new User();
userRepository.saveAndFlush(user);
userRepository.findAll().forEach(user1 -> {
System.out.println(user.getId());
assertThat(user1.getId()).startsWith("QQQ");
});
}
}
-
It is difficult to guarantee unique without registration service, but most systems not need to introduce complex registration service. If you have many services or need dynamic scaling, you can create a Redis-based Generator as follows.
-
Note: You can only choose one.
- Simple, does not depend on any service, does not support dynamic scaling. Suitable for where the number of servers is fixed and relatively small.
SnowflakeGenerator.build("server_1", List.of("server_1", "server_2"));
- High level, Requires Redis. Suitable for container drift or dynamic scaling. E.g Kubernetes | Docker | Serverless
// "redis://:password@host:port/database"
SnowflakeGenerator.build("redis://127.0.0.1:6379/0");
- When more than 4095 IDs are generated in 1 millisecond, the program will borrow the next 1 millisecond, and it will not exceed 1 second.