并发自定义主键生成策略

合集下载

Hibernate自定义主键生成策略

Hibernate自定义主键生成策略

Hibernate自定义主键生成策略
1问题场景
使用hibernate存储对象时,有时需要对根据自定义的主键进行排序等特殊用途,需要自定义主键生成方法。

一种简单方法是将对象的主键生成策略设置为“assigned”,在插入对象时由用户将自定义生成的ID通过set方法手动赋值给对象,但这种方法似乎存在隐患,实际开发中不推荐使用。

本文通过自定义的主键生成策略实现上述功能。

2问题解决
2.1 自定义ID生成策略类IDGenerator
新建生成策略类IDGenerator.java实现IdentifierGenerator, Configurable接口:
2.2 随机编码生产类RandomStringGenerator RandomStringGenerator.java:
2.3Entity中使用
上述IDGenerator类实现了IdentifierGenerator, Configurable接口,可以像Hibernate提供的主。

Hibernate各种主键生成策略与配置详解

Hibernate各种主键生成策略与配置详解

Hibernate各种主键生成策略与配置详解目录Hibernate各种主键生成策略与配置详解 (1)1、assigned (2)2、increment (2)3、hilo (2)4、seqhilo (3)5、sequence (4)6、identity (4)7、native (4)8、uuid (5)9、guid (5)10、foreign (6)11、select (6)12、其他注释方式配置 (6)13、小结 (7)1、assigned主键由外部程序负责生成,在 save() 之前必须指定一个。

Hibernate不负责维护主键生成。

与Hibernate和底层数据库都无关,可以跨数据库。

在存储对象前,必须要使用主键的setter方法给主键赋值,至于这个值怎么生成,完全由自己决定,这种方法应该尽量避免。

注释方式“ud”是自定义的策略名,人为起的名字,后面均用“ud”表示。

特点:可以跨数据库,人为控制主键生成,应尽量避免。

2、increment由Hibernate从数据库中取出主键的最大值(每个session只取1次),以该值为基础,每次增量为1,在内存中生成主键,不依赖于底层的数据库,因此可以跨数据库。

Hibernate调用org.hibernate.id.IncrementGenerator类里面的generate()方法,使用select max(idColumnName) from tableName语句获取主键最大值。

该方法被声明成了synchronized,所以在一个独立的Java虚拟机内部是没有问题的,然而,在多个JVM同时并发访问数据库select max时就可能取出相同的值,再insert就会发生Dumplicate entry的错误。

所以只能有一个Hibernate应用进程访问数据库,否则就可能产生主键冲突,所以不适合多进程并发更新数据库,适合单一进程访问数据库,不能用于群集环境。

generatedvalue()主键生成策略

generatedvalue()主键生成策略

generatedvalue()主键生成策略(原创实用版)目录1.主键生成策略概述2.主键生成策略的分类3.主键生成策略的优缺点4.主键生成策略的选择建议正文1.主键生成策略概述在数据库设计中,主键是唯一标识一条记录的字段,其作用是确保数据的唯一性和完整性。

主键生成策略,即如何为数据库中的记录分配主键的策略,是数据库设计中的重要环节。

合理的主键生成策略能够提高数据库性能、降低数据冗余和保证数据安全。

2.主键生成策略的分类主键生成策略可以分为以下几类:(1)自增主键:自增主键是最常见的主键生成策略,其特点是主键值随着记录的插入而递增。

这种策略简单易实现,但可能导致主键值的浪费和分布不均。

(2)固定值主键:固定值主键是指在记录插入前,先为表定义一组固定的主键值。

这种策略适用于数据量较小且不会频繁增加或删除的场景。

固定值主键可以避免主键值的浪费,但插入记录时需要预先分配主键,可能导致性能下降。

(3)唯一约束主键:唯一约束主键是指在表中定义一个或多个唯一约束的字段,以保证数据的唯一性。

这种策略适用于对数据唯一性要求较高的场景,但需要注意索引和性能的问题。

(4)序列主键:序列主键是指使用数据库中的序列(Sequence)为记录分配主键。

这种策略适用于需要确保主键值有序且不重复的场景,但可能存在性能和冗余的问题。

3.主键生成策略的优缺点(1)自增主键:优点是简单易实现,缺点是可能导致主键值的浪费和分布不均。

(2)固定值主键:优点是可以避免主键值的浪费,缺点是插入记录时需要预先分配主键,可能导致性能下降。

(3)唯一约束主键:优点是能保证数据的唯一性,缺点是需要注意索引和性能的问题。

(4)序列主键:优点是确保主键值有序且不重复,缺点是可能存在性能和冗余的问题。

4.主键生成策略的选择建议(1)根据业务场景选择:根据实际业务需求和数据特点,选择适合的主键生成策略。

(2)考虑性能和扩展性:在选择主键生成策略时,应充分考虑其对数据库性能和扩展性的影响。

分库分表主键生成策略

分库分表主键生成策略

分库分表主键生成策略分库分表是数据库架构中的一种技术,用于解决单一数据库的性能瓶颈和数据量过大的问题。

通过将数据分散到多个数据库或表中,可以提高系统的并发处理能力和数据存储能力。

在分库分表的场景下,主键生成策略是关键的一环。

合理的主键生成策略可以确保数据的唯一性、连续性,并减少跨库跨表的事务冲突。

以下是一些常见的主键生成策略:1. 自增主键:这是最常见的主键生成策略。

在每个分库分表内部,都有一个自增的ID作为主键。

这种策略的优点是简单易用,且能保证数据的唯一性。

但缺点是跨库分表的事务冲突概率较高。

2. 雪花算法(Snowflake):Twitter开源的雪花算法可以生成全局唯一的ID。

它通过结合时间戳、机器ID、序列号等参数,确保生成的ID在全局范围内是唯一的。

这种策略适用于需要全局唯一ID的场景。

3. UUID:全局唯一标识符UUID可以作为主键。

UUID的优点是全局唯一,不会产生冲突。

但缺点是UUID较长,可能会影响查询性能,且不易于人类阅读。

4. 基于时间的主键:结合时间戳和一定的随机数或序列号,生成一个唯一的主键。

这种策略适用于对时间有要求的业务场景。

5. 组合主键:使用多个字段的组合作为主键。

这种策略适用于业务上具有唯一性的字段组合。

6. 分布式ID生成器:例如基于Redis的ID生成器,或者基于ZooKeeper 的ID生成器等。

这些工具可以生成全局唯一的ID,并确保在高并发下的性能和稳定性。

选择哪种主键生成策略取决于业务需求、系统复杂性和性能要求等因素。

在实施分库分表时,建议充分考虑这些因素,并可能需要对多种策略进行实验和测试,以找到最适合自己业务场景的方案。

hibernate uuid原理

hibernate uuid原理

hibernate uuid原理摘要:1.Hibernate UUID 简介2.Hibernate UUID 的生成原理3.Hibernate UUID 的应用场景4.Hibernate UUID 与数据库主键的关系5.如何自定义Hibernate UUID 生成策略正文:Hibernate 是一个广泛应用于Java 应用程序中的对象关系映射(ORM)框架。

在Hibernate 中,UUID(通用唯一标识符)被广泛应用于数据库表的主键生成。

本文将详细介绍Hibernate UUID 的原理、应用场景以及如何自定义生成策略。

1.Hibernate UUID 简介Hibernate UUID 是Hibernate 框架提供的一种生成主键的策略。

它可以生成一个32 位的、全局唯一的、时间戳无关的UUID。

UUID 由三部分组成:随机数、时间戳和命名空间。

其中,命名空间用于区分不同类型的UUID,例如:DNS、URL 等。

在Hibernate 中,我们主要关注的是随机数和时间戳。

2.Hibernate UUID 的生成原理Hibernate UUID 的生成原理主要依赖于Java 中的UUID 类。

UUID 类提供了两个静态方法:randomUUID() 和timeBasedUUID()。

randomUUID() 方法生成一个基于随机数的UUID,而timeBasedUUID() 方法生成一个基于时间戳的UUID。

Hibernate UUID 策略实际上是将这两种UUID 组合起来,生成一个新的UUID。

3.Hibernate UUID 的应用场景Hibernate UUID 主要应用于数据库表的主键生成。

在Hibernate 中,可以通过配置映射文件或使用注解来指定主键生成策略。

当使用Hibernate UUID 作为主键生成策略时,Hibernate 会自动为数据库表生成一个唯一的UUID 作为主键。

generatedvalue()主键生成策略

generatedvalue()主键生成策略

generatedvalue()主键生成策略一、生成主键的意义在数据库系统中,主键是用来唯一标识一条记录的字段。

生成主键的目的是确保数据表中的记录具有唯一性,避免重复数据的出现。

主键可以是自然主键(如身份证号码、订单编号等),也可以是人工设置的主键(如自增ID)。

二、生成主键的方法1.自增ID自增ID是一种常用的生成主键的方法。

在数据库中创建一个整数类型的字段,每次插入新记录时,由数据库自动为该字段赋值。

自增ID的优点是简单易实现,缺点是当数据表中已存在大量记录时,ID的生成速度会受到影响。

2.UUIDUUID(Universally Unique Identifier)是一种通用唯一标识符,可用于生成全局唯一的主键。

UUID的生成方式有很多种,如使用时间戳、MD5散列等。

UUID的优点是生成的字符串长度较短,便于存储和传输;缺点是UUID 的字符串形式可能含有特殊字符,不利于数据库字段的美观。

3.数据库生成策略数据库生成策略是指利用数据库自身的功能来生成主键。

例如,在MySQL中使用AUTO_INCREMENT关键字,Oracle中使用SEQUENCE对象等。

这种方法的优点是可以充分利用数据库的性能,缺点是需要对数据库进行额外的配置。

三、生成主键的优缺点1.自增ID优点:- 简单易实现,无需额外配置;- 在小规模数据表中,性能影响较小。

缺点:- 当数据表规模较大时,ID生成速度减慢;- 数字类型的ID可能存在重复可能性。

2.UUID优点:- 生成的字符串长度较短,便于存储和传输;- 全局唯一性,避免ID重复。

缺点:- UUID的字符串形式可能含有特殊字符,不利于数据库字段的美观;- 生成速度较慢,性能影响较大。

3.数据库生成策略优点:- 充分利用数据库性能,提高ID生成速度;- 自动维护主键顺序,无需额外操作。

缺点:- 需要对数据库进行额外的配置;- 在某些数据库中,可能存在并发冲突的问题。

四、适用场景的选择在实际项目中,选择生成主键的方法需根据项目需求和数据库特性进行权衡。

generatedvalue()主键生成策略

generatedvalue()主键生成策略

generatedvalue()主键生成策略`@GeneratedValue`是Java持久化(JPA)中用于指定主键生成策略的注解。

它通常与`@Id`注解一起使用,用于表示实体类的主键字段。

在JPA中,`@GeneratedValue`主要用于指定主键的生成方式。

常见的主键生成策略包括:1.IDENTITY:使用数据库的自增长列。

适用于支持自增列的数据库,如MySQL、SQL Server。

```java@Id@GeneratedValue(strategy=GenerationType.IDENTITY)private Long id;```2.SEQUENCE:使用数据库的序列。

适用于支持序列的数据库,如Oracle。

```java@Id@GeneratedValue(strategy=GenerationType.SEQUENCE,generator="sequence_generator")@SequenceGenerator(name="sequence_generator",sequenceName="custom_sequence")private Long id;```3.TABLE:使用数据库表模拟序列。

适用于不支持序列或自增列的数据库。

```java@Id@GeneratedValue(strategy=GenerationType.TABLE,generator="table_generator")@TableGenerator(name="table_generator",table="id_generator_table", pkColumnName="gen_name",valueColumnName="gen_value",allocationSize=1)private Long id;```这些例子中的`@GeneratedValue`注解中的`strategy`属性用于指定生成策略。

generatedvalue生成策略

generatedvalue生成策略

generatedvalue生成策略GeneratedValue生成策略是Java中的一种机制,用于定义在自动为实体生成主键时所使用的策略。

该机制有助于减轻开发人员的工作负担,同时确保数据库的一致性和完整性。

在Java中,我们通常使用关系型数据库来存储数据。

每个表都有一个主键,作为数据的唯一标识。

通常情况下,主键是由开发人员手动分配的。

这种方法需要开发人员持续的、人工的投入,而且可能存在主键数据覆盖的风险。

为了解决这些问题,开发人员可以使用自动生成主键的方法。

@GeneratedValue生成策略使开发人员无需手动分配主键。

相反,它让开发人员定义在自动分配主键时使用的策略。

Java中提供了多种GeneratedValue生成策略。

下面我们将详细讨论这些策略。

1. AUTOAUTO是默认的GeneratedValue生成策略,它表示实现程序(如Hibernate)自动选择最适合底层数据库的策略。

例如,在MySQL中,它将为您选择Identity Identity;而容器支持分配的方式,如在EJB2.x使用容器分配主键。

2. IDENTITYIDENTITY策略在数据库表中使用自增长列。

当插入行时,数据库会自动生成自增列的值,并将其赋值给主键列。

IDENTITY通常在Oracle和SQL Server中使用。

3. TABLETABLE策略通过一个特定的数据库表来维护主键生成。

此表将有一列来存储下一个可用的主键值。

每次需要生成新的主键时,程序将请求此表的下一个可用主键值。

这种方法可以在任何数据库中使用,但是它会增加数据库的负担,可能会导致性能问题。

4. SEQUENCESEQUENCE策略与TABLE策略非常相似。

它使用数据库的序列来生成主键值。

当需要生成新的主键时,程序将请求下一个序列值。

SEQUENCE策略通常在Oracle中使用。

5. UUIDUUID策略使用Java的UUID生成器来生成主键值。

并发自定义主键生成策略

并发自定义主键生成策略

并发自定义主键生成策略并发自定义主键生成策略在项目开发中,我遇到一个需求.就是要生成自定义的主键.主键的格式为:前缀+当天日期+自增长序列号自增长序列号:这个序列号是每天从0或1开始自增长的在了解完需要之后,我做了简单的分析之后就马上进入编码模式,这里我使用的是mysql数据,全部代码已经上传到github,有需要的同学自行下载目录结构(项目管理工具用的maven)srcmainjavacom.kco.bean.SequenceNumberBeancom.kco.dao.Sequence NumberDaocom.kco.Enum.SequenceNumberEnumcom.kco.serv ice.SequenceNumberServicecom.kco.service.SequenceNumberS erviceImplresourcesMETA-INF/mybatis/mapper/SequenceNumberDao.xmlMETA-INF/mybatis/sql-map-config.xmlMETA-INF/spring/spring-base-jdbc.xmllog4j.propertiestestjavacom.kco.TestSequenceNumberServicepom.xml编码1,首先创建一个maven工程,配置一下jar依赖pom.xml4.0.0com.kcomytestcode1.0-SNAPSHOTmons commons-lang33.3.2mons commons-io1.3.2mons commons-collections4 4.1mons commons-dbcp22.0.1mons commons-pool22.3junitjunit4.10testorg.springframework spring-testtest4.2.3.RELEASE mysqlmysql-connector-java 5.1.12runtimeorg.mybatis mybatis3.2.7org.springframework spring-context4.2.3.RELEASEorg.springframework spring-context-support 4.2.3.RELEASEorg.springframework spring-tx4.2.3.RELEASEorg.springframework spring-jdbc4.2.3.RELEASEorg.springframework spring-beans4.2.3.RELEASEorg.springframework spring-aop4.2.3.RELEASEorg.aspectj aspectjrt1.8.8org.aspectj aspectjtools 1.8.8org.aspectj aspectjweaver 1.8.8org.mybatis mybatis-spring1.2.2log4jlog4j1.2.17org.slf4jslf4j-log4j12 1.7.13org.slf4jslf4j-apiorg.slf4jjcl-over-slf4jruntime1.7.13org.apache.maven.plugins maven-compiler-plugin1.81.8UTF-8org.apache.maven.plugins maven-resources-plugin 2.6tbl2.,初始化数据库/schema/beans/spring-beans.xsd/schema/context/schema/context/spring-context.xsd/schema/aop/schema/aop/spring-aop.xsd/schema/tx/schema/tx/spring-tx.xsd">4,配置mybatissql-map-config.xml5,因为要生成自定义主键一般在开发阶段就已经能确定,所以我这里使用了一个枚举类来管理所有的自定义主键com.kco.Enum.SequenceNumberEnumpackagecom.kco.Enum;importcom.kco.bean.SequenceNumberBean;/com.kco.EnumCreatedbyswlvon2016/10/25./publicenumSequenceNumberEnum{GD(newSequenceNumberBean("GD","工单主键生成策略",1,1,8));privateSequenceNumberBeansequenceNumberBean;SequenceNumberEnum(SequenceNumberBeansequenceNu mberBean){this.sequenceNumberBean=sequenceNumberBean;publicSequenceNumberBeangetSequenceNumberBean(){ returnsequenceNumberBean;}}6,数据库PUB_SEQUENCE_NUMBER对应的bean类com.kco.bean.SequenceNumberBeanpackagecom.kco.bean;/主键生成策略的beancom.kco.beanCreatedbyswlvon2016/10/25./ publicclassSequenceNumberBean{privateStringprefix; privateStringname; privateStringtoday; privateintminNum; privateintcurrentNum; privateintnumLength;publicStringgetPrefix(){ returnprefix;}publicvoidsetPrefix(Stringprefix){this.prefix=prefix;}publicStringgetName(){ returnname;}publicvoidsetName(Stringname){ =name;}publicStringgetToday(){ returntoday;}publicvoidsetT oday(Stringtoday){ this.today=today;}publicintgetMinNum(){ returnminNum;}publicvoidsetMinNum(intminNum){ this.minNum=minNum;}publicintgetCurrentNum(){ returncurrentNum;}publicvoidsetCurrentNum(intcurrentNum){ this.currentNum=currentNum;}publicintgetNumLength(){ returnnumLength;}publicvoidsetNumLength(intnumLength){ this.numLength=numLength;}publicSequenceNumberBean(){}publicSequenceNumberBean(Stringprefix,Stringname,intmi nNum,intcurrentNum,intnumLength){this.prefix=prefix;=name;this.minNum=minNum;this.currentNum=currentNum;this.numLength=numLength;}@OverridepublicStringtoString(){return"SequenceNumberBean{"+"prefix=''"+prefix+''\''''+",name=''"+name+''\''''+",today=''"+today+''\''''+",minNum="+minNum+",currentNum="+currentNum+",numLength="+numLength+''}'';}}7,服务层的接口类以及实现com.kco.service.SequenceNumberServicepackagecom.kco.service;importcom.kco.Enum.SequenceNumberEnum;/com.kco.serviceCreatedbyswlvon2016/10/25./publicinterfaceSequenceNumberService{/生成一个主键@paramsequenceNumberEnum主键生成类型@return返回一个生成的主键/StringnewSequenceNumber(SequenceNumberEnumsequen ceNumberEnum);}com.kco.service.SequenceNumberServiceImplpackagecom.kco.service;importcom.kco.Enum.SequenceNumberEnum;importcom.kco.bean.SequenceNumberBean;importcom.kco.dao.SequenceNumberDao;importorg.springframework.stereotype.Service;importjavax.annotation.Resource;/com.kco.serviceCreatedbyswlvon2016/10/25./@ServicepublicclassSequenceNumberServiceImplimplementsSequen ceNumberService{@ResourceprivateSequenceNumberDaosequenceNumberDao;@OverridepublicsynchronizedStringnewSequenceNumber(SequenceN umberEnumsequenceNumberEnum){if(sequenceNumberEnum==null){returnnull;}try{SequenceNumberBeansequenceNumberBean=sequenceNu mberDao.newSequenceNumber(sequenceNumberEnum.getSeq uenceNumberBean().getPrefix());Thread.sleep(100);if(sequenceNumberBean==null){sequenceNumberBean=sequenceNumberEnum.getSequenc eNumberBean();sequenceNumberBean.setT oday(sequenceNumberDao.getT oday());Thread.sleep(100);sequenceNumberDao.updateSequenceNumber(sequenceN umberBean);Thread.sleep(100);returnString.format("%s%6s%08d",sequenceNumberBean.getPrefix(),sequenceNumberBean.ge tToday(),CurrentN um());}catch(InterruptedExceptione){e.printStackTrace();return"";}}}8,数据库dao层接口com.kco.dao.SequenceNumberDaopackagecom.kco.dao;importcom.kco.bean.SequenceNumberBean; importorg.apache.ibatis.annotations.Param; importorg.springframework.stereotype.Repository;/com.kco.daoCreatedbyswlvon2016/10/25./@Repository publicinterfaceSequenceNumberDao{/根据前缀生成一个序列号信息@paramprefix前缀@return新的序列号信息/SequenceNumberBeannewSequenceNumber(@Param("pref ix")Stringprefix);/将生成的序列号信息更新到数据库中@paramsequenceNumberBean需要更新信息/voidupdateSequenceNumber(@Param("bean")SequenceNu mberBeansequenceNumberBean);/获取数据库当天日期@return/StringgetT oday();}9.,dao层mybatis的实现SequenceNumberDao.xmlSELECTprefix,name,today,minNum,currentNum+1ascurrent Num,numLengthFROMPUB_SEQUENCE_NUMBERWHEREprefix=#{prefix}ANDtoday=DATE_FORMAT(CURRENT_DATE(),''%Y%m%d'')REPLACEINTOPUB_SEQUENCE_NUMBER(prefix,name,today, minNum,currentNum,numLength)VALUES(#{bean.prefix},#{},D ATE_FORMAT(CURRENT_DATE(),''%Y%m%d''),#{bean.minNum},# {bean.currentNum},#{bean.numLength})SELECTDATE_FORMAT(CURRENT_DATE(),''%Y%m%d'')10,编写测试代码(模拟并发测试)com.kco.TestSequenceNumberServicepackagecom.kco;importcom.kco.Enum.SequenceNumberEnum;importcom.kco.service.SequenceNumberService;mons.collections4.CollectionUtils;importorg.springframework.context.ApplicationContext;importorg.springframework.context.support.ClassPathXmlA pplicationContext;importjava.util.ArrayList;importjava.util.Collections;importjava.util.List;importjava.util.Queue;importjava.util.concurrent.ArrayBlockingQueue;/com.cmbchina.base.serviceCreatedbyswlvon2016/10/25./publicclassTestSequenceNumberService{ publicstaticvoidmain(String[]args)throwsInterruptedException{ApplicationContextapplicationContext=newClassPathXmlAp plicationContext("META-INF/spring/spring-base-jdbc.xml");SequenceNumberServicesequenceNumberService=applicati onContext.getBean(SequenceNumberService.class);Queuequeue=newArrayBlockingQueue(200);Listlist=newArrayList<>();for(inti=0;i<200;i++){list.add(newThread(()->{Stringkey=sequenceNumberService.newSequenceNumber( SequenceNumberEnum.GD);queue.add(key);}));}for(Threadthread:list){thread.start();}while(queue.size()!=200);System.out.println(queue);}}11,至此整个项目就已经搭建完成细节解析在com.kco.service.SequenceNumberServiceImpl#newSequenceNu mber的实现中在更新当天日期字符串时,使用的是sequenceNumberBean.setT oday(sequenceNumberDao.getToday ());这是因为服务器的时间跟数据库的时间有可能不一致,这里以数据库的时间为准测试1,查询数据库的数据SELECTcount(1)FROMPUB_SEQUENCE_NUMBER结果:count(1)2,清空数据后,再运行com.kco.TestSequenceNumberService#main3,然后查一下数据库SELECTFROMPUB_SEQUENCE_NUMBERprefix NAME today minNum currentNum numLengthGD 工单主键生成策略 20161026 1 176 8问题出现了,明明我是启动了200个线程在跑,怎么才生成176个主键,检查代码也没有问题啊,newSequenceNumber也加了关键字synchronized.先把这个问题放一下,来回忆一下synchronized的用法,synchronized是在线程池在访问共同变量时使其加锁,达到互斥的效果共同变量?共同变量?咦,好像有什么不对劲的地方.newSequenceNumber使用的都是局部变量,没有共同变量啊!不对?是有共同变量,那个共同变量就是数据库的记录除此之外使用synchronized还有一个问题,这个项目是web项目的一部分,一般在部署web项目都是有几个单边,即在不同的PC部署几个一模一样的代码,再通过集群访问的方式随机访问其中一台.那么这几个应用其实就是多进程程序,都不再一个JVM中执行,加synchronized 来保证互斥,是不够的.4,既然知道是数据库同步出现了问题,那么怎么保证数据库同步呢?上网搜索一下,发现数据库有悲观锁和乐观锁解决方法就是在Dao层的实现SELECTprefix,name,today,minNum,currentNum+1ascurrent Num,numLengthFROMPUB_SEQUENCE_NUMBERWHEREprefix=#{prefix}ANDtoday=DATE_FORMAT(CURRENT_DATE(),''%Y%m%d'') 改为SELECTprefix,name,today,minNum,currentNum+1ascurrent Num,numLengthFROMPUB_SEQUENCE_NUMBERWHEREprefix=#{prefix}ANDtoday=DATE_FORMAT(CURRENT_DATE(),''%Y%m%d'')f orUPDATE5,然后再重新查一下数据库SELECTFROMPUB_SEQUENCE_NUMBERprefix NAME today minNum currentNum numLengthGD 工单主键生成策略 20161026 1 200 8发现问题解决了,这里是用到了数据库锁表的机制来实现同步.以后有机会再学一下数据库同步,悲观锁和乐观锁.因为这个我还不太熟悉,这里就不卖弄了.6.,修改一下com.kco.Enum.SequenceNumberEnum再增加一个自定义主键GDD(newSequenceNumberBean("GDD","工单主键生成策略",1,1,8))7.,清空数据,然后运行com.kco.TestSequenceNumberService#main8,修改com.kco.TestSequenceNumberService2#main将Stringkey=sequenceNumberService.newSequenceNumber(Sequ enceNumberEnum.GD);改为Stringkey=sequenceNumberService.newSequenceNumber(Sequ enceNumberEnum.GDD);再次运行com.kco.TestSequenceNumberService#main,这是用来模拟两个程序同时访问数据的情况9,查询数据SELECTFROMPUB_SEQUENCE_NUMBERprefix NAME today minNum currentNum numLengthGD 工单主键生成策略 20161026 1 200 8GDD 工单主键生成策略 20161026 1 200 8数据正常10.,清空数据,连续运行两次com.kco.TestSequenceNumberService2#main模拟多进程对生成同一个自定义主键11,查询结果SELECTFROMPUB_SEQUENCE_NUMBERprefix NAME today minNum currentNum numLengthGDD 工单主键生成策略 20161026 1 400 8至此,全部搞定遗留问题。

MySQL中的主键生成策略和自增字段应用

MySQL中的主键生成策略和自增字段应用

MySQL中的主键生成策略和自增字段应用MySQL是一种常用的关系型数据库管理系统,它广泛应用于各类应用程序中,尤其在Web开发领域。

在MySQL中,主键的生成策略和自增字段的应用是一个非常重要的主题。

本文将从理论角度出发,探讨MySQL中主键生成策略的原理和常见的应用场景。

首先,我们需要明确主键的概念。

主键是关系数据库中用来唯一标识一条记录的字段或字段组合。

在MySQL中,主键可以由多个字段组成,也可以使用单个字段。

主键的作用不仅仅是唯一标识一条记录,还可以用于优化查询性能。

在MySQL中,主键的生成策略有多种。

其中最常见的是使用自增字段。

自增字段是一种特殊的字段类型,在每次插入新记录时,MySQL会自动为该字段赋予一个唯一的值。

这个值通常是一个整数,在每次插入新记录时自动增加。

使用自增字段作为主键有几个优点。

首先,主键的值是自动生成的,不需要手动指定。

这样可以简化插入记录的过程。

其次,自增字段的值是按照一定顺序递增的,这样可以提高插入记录的效率。

当插入大量记录时,不需要指定主键值,MySQL会自动递增生成主键值,这样可以减少数据库操作的负担。

除了自增字段,还可以使用UUID作为主键生成策略。

UUID是一种全局唯一标识符,由网络中的不同节点生成。

UUID的生成算法是根据当前的时间戳、计算机的MAC地址和一些其他参数生成的,保证了生成的值是唯一的。

使用UUID作为主键的优点是可以在分布式系统中保证主键的唯一性。

在分布式系统中,每个节点生成的主键都是唯一的,不会出现重复的情况。

这样可以方便地将数据分布到不同的节点上,提高系统的可扩展性。

除了自增字段和UUID,还可以使用其他生成策略作为主键。

例如,可以使用其他业务字段作为主键,如用户名、手机号码等。

这种方式适用于一些需要业务字段和主键之间存在某种关联关系的场景。

但是需要注意的是,业务字段作为主键必须保证其唯一性,并且需要进行额外的校验操作。

在实际应用中,根据实际需求选择合适的主键生成策略非常重要。

MySQL数据库主键生成策略与选择方法

MySQL数据库主键生成策略与选择方法

MySQL数据库主键生成策略与选择方法引言:MySQL作为一款广泛使用的关系型数据库管理系统,主键的选择和生成策略在数据库设计中扮演着至关重要的角色。

一个合理的主键生成策略可以提高数据库性能和数据完整性,而不恰当的选择则会带来一系列问题。

本文将围绕MySQL数据库主键生成策略和选择方法展开讨论。

一、主键的重要性和作用:主键是数据库中用来唯一标识每一条记录的字段或字段组合。

它的作用有以下几点:1. 唯一标识:主键能够确保每条记录都有唯一的标识符,避免数据冗余和重复。

2. 数据完整性:主键约束可以阻止插入和更新操作中出现重复的数据,确保数据的完整性和准确性。

3. 数据索引:主键字段通常会自动创建索引,提高查询和数据检索的效率。

二、主键生成策略的选择:MySQL提供了多种主键生成策略,根据业务需求和数据特点选择适合的策略可以提高数据库性能和开发效率。

1. 自增主键(Auto Increment):自增主键是最常见的一种主键生成策略,通过自动递增的方式为每条记录分配一个唯一的标识符。

可以选择INT、BIGINT或其他整数类型作为主键,并将其属性设置为AUTO_INCREMENT。

自增主键适合于需要快速插入大量数据的场景,同时能够保证主键的唯一性。

2. UUID(Universally Unique Identifier)主键:UUID主键使用128位的全局唯一标识符,可以通过UUID()函数生成。

与自增主键相比,UUID主键不依赖于数据库自增功能,可以在分布式环境中保证全局唯一性。

但是,UUID主键会占用较大的存储空间,并且在索引性能上相对较差,不适合大规模数据的场景。

3. 组合主键:组合主键是使用多个字段作为主键,可以通过唯一联合索引来确保主键的唯一性。

组合主键适合于需要多个字段来唯一标识记录的场景,例如复合实体和关联表。

三、主键选择的注意事项:在选择主键和生成策略时,需要考虑以下几个方面:1. 数据特点和业务需求:不同的数据特点和业务需求对主键的选择具有一定的指导意义。

4.ID主键生成策略

4.ID主键生成策略

4.ID主键⽣成策略保证唯⼀性(auto_increment) ⼀、xml⽅式1<?xml version="1.0"?>2<!DOCTYPE hibernate-mapping PUBLIC3 "-//Hibernate/Hibernate Mapping DTD 3.0//EN"4 "/hibernate-mapping-3.0.dtd">56<hibernate-mapping package="com.bjsxt.hibernate.model">7<class name="Student" table="t_student" dynamic-update="true">8<id name="id">9<generator class="native"></generator>10</id>1112<property name="name"></property>13</class>14</hibernate-mapping> <id> 标签必须配置在 <class>标签内第⼀个位置。

由⼀个字段构成主键,如果是复杂主键<composite-id> 标签  <generator> 元素(主键⽣成策略) 如:<generator class="native"/> 根据是什么数据库,⾃动选择,mysql默认是 'identity' 即 auto_increment  <generator class="uuid "/> ⽤⼀个128-bit的UUID算法⽣成字符串类型的标识符在Mysql类型是 varchar(255) ⼆、annotation⽅式 在相应的get⽅法上使⽤@GeneratedValues(strategy=GeneratedType) strategy 取值:如:strategy=GenerationType.AUTO 1.AUTO(默认值) - 可以是identity column类型,或者sequence 类型或者 table 类型取决于不同的底层数据库相当于 native 1、对于mysql,使⽤auto_increment 2、对于oracle使⽤hibernate_sequence(名称固定) 2.TABLE - 使⽤表保存id值 就是在数据库中建⽴⼀个表,这个表包含两个字段,⼀个字段表⽰名称,另⼀个字段表⽰值。

主键(id)生成策略

主键(id)生成策略

主键(id)⽣成策略
1)数据库⾃动增长 auto increment
优点:
简单⽅便
数字id天然排序,对分页或结果需要排序很有帮助
缺点:
不同数据库语法和实现不同,数据库迁移的时候或多版本⽀持的时候需要处理
在单个数据库或读写分离或⼀主多从的情况下,只有⼀个主库可以⽣成,有单点故障的风险 ⽐较难于扩展
分库分表⽐较⿇烦
2) UUID 每次⽣成随机的唯⼀的值
优点:
简单⽅便
⽣成ID性能⾮常好,基本不会有性能问题
全球唯⼀,在数据迁移,系统数据合并或者数据库变更情况下,容易应对
缺点:
没有排序,⽆法保证递增趋势
使⽤字符串存储,查询的效率⽐较低
传输数据量⼤
3)redis实现
主要依赖redis是单线程的,所以也可以⽤于⽣成全局唯⼀的ID。

INCR 和 INCRBY
优点:
不依赖数据库,灵活⽅便,且性能优于数据库
数据id天然排序
缺点:
如果系统中没有redis,还需要引⼊新的组件
需要编码和配置的⼯作量⽐较⼤
4) 雪花算法 snowflake (mybatis-plus默认策略)
⼀个long型的ID
优点:
不依赖于数据库,灵活⽅便,性能优于数据库
ID按照时间在单机上是递增的
缺点:
如果设计分布式环境,每台集器上的时钟不可能完全同步,有时候不能保证全局递增。

小书MybatisPlus第6篇-主键生成策略精讲

小书MybatisPlus第6篇-主键生成策略精讲

⼩书MybatisPlus第6篇-主键⽣成策略精讲本⽂为mybatis系列⽂档的第6篇,前5篇请访问下⾯的⽹址。

Mybatis Plus 为我们提供了三种设置主键⽣成策略的⽅式。

它们的优先级顺序是:局部注解 > 全局 > 默认(雪花算法)。

下⾯我们来⼀⼀介绍⼀、默认主键⽣成策略:雪花算法Mybatis Plus如果不做任何主键策略配置,默认使⽤的是雪花算法。

该策略会根据雪花算法⽣成主键ID,主键类型为Long或String(具体到MySQL数据库就是BIGINT和VARCHAR),该策略使⽤接⼝IdentifierGenerator的⽅法nextId(默认实现类为DefaultIdentifierGenerator雪花算法)snowflake算法是Twitter开源的分布式ID⽣成算法,结果是⼀个long类型的ID 。

其核⼼思想:使⽤41bit作为毫秒数,10bit作为机器的ID(5bit数据中⼼,5bit的机器ID),12bit作为毫秒内的流⽔号(意味着每个节点在每个毫秒可以产⽣4096个ID),最后还有⼀个符号位,永远是0。

⼆、⾃定义主键策略mybatis-plus3.3.0以后,主要有五种主键⽣成策略。

public enum IdType {/*** 数据库ID⾃增,数据库需要⽀持主键⾃增(如MySQL),并设置主键⾃增*/AUTO(0),/*** 该类型为未设置主键类型,默认使⽤雪花算法⽣成*/NONE(1),/*** ⽤户输⼊ID,数据类型和数据库保持⼀致就⾏* <p>该类型可以通过⾃⼰注册⾃动填充插件进⾏填充</p>*/INPUT(2),/* 以下3种类型、只有当插⼊对象ID 为空,才⾃动填充。

*//*** 全局唯⼀ID (idWorker),数值类型数据库中也必须是数值类型否则会报错*/ID_WORKER(3),/*** 全局唯⼀ID (UUID,不含中划线)*/UUID(4),/*** 字符串全局唯⼀ID (idWorker 的字符串表⽰),数据库也要保证⼀样字符类型*/ID_WORKER_STR(5);}三、局部注解配置策略我们针对主键设置主键策略使⽤注解⽅式为@TableId(type = IdType.AUTO)private long userId;四、全局配置策略mybatis-plus:global-config:db-config:id-type: auto五、扩展使⽤5.1.INPUT⽤户输⼊ID策略的⽤法其中需要和⼤家特殊介绍的是:Input(⽤户输⼊ID),这个ID来源可以有两种⽤户⾃⼰设置ID,并在insert之前SET主键的值⼀些有序列的数据库,⽐如Oracle,SQLServer等,针对这些数据库我们可以通过序列填充ID字段Mybatis-Plus 内置了如下数据库主键序列(如果内置⽀持不满⾜你的需求,可实现 IKeyGenerator 接⼝来进⾏扩展):DB2KeyGeneratorH2KeyGeneratorKingbaseKeyGeneratorOracleKeyGeneratorPostgreKeyGenerator以Oracle 的Sequence使⽤⽅法为例,使⽤⽅法如下:⾸先添加@Bean@Beanpublic OracleKeyGenerator oracleKeyGenerator(){return new OracleKeyGenerator();}然后实体类配置主键 Sequence,指定主键策略为 IdType.INPUT 即可,@Data@KeySequence(value = "SEQ_USER" , clazz = Long.class)public class User {@TableId(value = "ID",type = IdType.INPUT)private Integer id;欢迎关注我的博客,⾥⾯有很多精品合集本⽂转载注明出处(必须带连接,不能只转⽂字):。

主键生成策略

主键生成策略

主键生成策略以《主键生成策略》为标题,写一篇3000字的中文文章一、引言由于数据库的发展和应用变得越来越复杂,主键生成策略也发生了显著的变化,它成为系统设计中不可或缺的一部分。

在本文中,我将深入探讨主键生成策略,主要包括它的定义、作用、实现方式等内容。

二、定义与作用主键(Primary Key)是指数据库表中各行数据所能用来区分和连接起来的唯一标识符,其本质是一种可以唯一确定记录的键,是数据库设计的基础。

一般来说,数据库表都会有一个主键,主键可以是单一的字段,也可以涉及多个字段,甚至可以是一组字段复合键或联合键。

主键生成策略就是用来生成数据表的主键的一种策略。

主键的主要作用有:1.数据索引:主键可以担任数据的索引,提高数据查询的效率;2.唯一性:主键具有唯一性,可以有效防止数据重复,提高数据的准确性;3.安全性:主键可以有效防止数据被篡改,从而保证数据的安全性。

三、实现方式1.自增长(Auto Increment)最常用的主键生成策略,一般使用整数作为主键。

它的作用是从特定的起始值开始,自动地将每次新增记录的主键值加1,这样就可以确保每条记录都有一个唯一的标识。

优点:自增长是最常用的主键生成策略,它使用简单,实现快速。

缺点:自增长主键容易被猜到,如果泄露给恶意用户,可能会带来安全问题。

2.UUID(Universally Unique Identifier)UUID是一种128位的标识符,由一个16字节的数组表示,它可以保证时间和空间的唯一性。

UUID的优点是比自增长更具唯一性且不易被猜测,它使用了一种叫做“GUID”(Globally Unique Identifier)的算法来生成。

优点:UUID算法较为安全,可以保证唯一性,不需要考虑时间戳的全局唯一性。

缺点:UUID的长度较长,占用内存资源较大。

3.GUID(Globally Unique Identifier)GUID是一种128位的全局唯一标识符,它可以保证全球范围内的唯一性,是由一个32位的数字组成,也可以由一个字符串来表示。

efcore 主键生成策略

efcore 主键生成策略

efcore 主键生成策略EF Core是一个开源的、跨平台的对象关系映射(ORM)框架,它提供了许多方便的功能来简化数据库操作。

在EF Core中,我们需要定义实体的主键生成策略,即指定如何生成主键值。

本文将详细介绍EF Core中常用的主键生成策略,并分析它们的优缺点。

1. 自增(Identity)自增是最常用的主键生成策略之一。

当我们将一个整型属性标记为主键,并将其类型设置为自增(如`[Key]`和`[DatabaseGenerated(DatabaseGeneratedOption.Identity)]`),EF Core会自动为该实体生成一个自增的主键值。

这种策略简单易用,适用于大多数情况。

但它有一个限制:只能用于整型主键。

2. GUID(SequentialGuid)GUID(全局唯一标识符)是一种用于唯一标识实体的策略。

在EF Core中,我们可以使用`Guid.NewGuid()`方法生成一个新的GUID 值作为主键。

然而,由于GUID是一个128位的标识符,它比整型主键更长,因此在数据库中存储和索引的效率会稍低。

为了解决这个问题,EF Core提供了一个称为SequentialGuid的库,它可以生成有序的GUID值。

有序GUID是一种特殊的GUID,它的值基于时间和MAC地址,保证了生成的GUID在一定程度上是有序的。

使用SequentialGuid库,我们可以通过调用`SequentialGuid.NewGuid()`方法生成有序的GUID作为主键值,从而提高数据库的性能。

3. 组合键(Composite Key)在某些情况下,一个实体可能需要由多个属性组成的复合键来唯一标识。

在EF Core中,我们可以使用`[Key]`和`[Column]`属性来标记多个属性作为复合键。

例如:```public class Order{[Key]public int OrderId { get; set; }[Key][Column(TypeName = "varchar(100)")]public string CustomerId { get; set; }}```这样,EF Core会将`OrderId`和`CustomerId`两个属性作为复合键来唯一标识`Order`实体。

Java 持久化技术规范(JPA )中的主键生成策略

Java 持久化技术规范(JPA )中的主键生成策略

Table 策略 (Table strategy)这种策略中,持久化引擎 (persistence engine) 使用关系型数据库中的一个表 (Table) 来生成主键。

这种策略可移植性比较好,因为所有的关系型数据库都支持这种策略。

不同的J2EE 应用服务器使用不同的持久化引擎。

下面用一个例子来说明这种表生成策略的使用:清单 1.Table 生成策略@Entitypublic class PrimaryKey_Table {@TableGenerator(name = "PK_SEQ",table = "SEQUENCE_TABLE",pkColumnName = "SEQUENCE_NAME",valueColumnName = "SEQUENCE_COUNT")@Id@GeneratedValue(strategy =GenerationType.TABLE,generator="PK_SEQ")private Long id;//Getters and Setters//为了方便,类里面除了一个必需的主键列,没有任何其他列,以后类似}首先,清单 1 中使用 @javax.persistence.TableGenerator 这个注解来指定一个用来生成主键的表 (Table),这个注解可以使用在实体类上,也可以像这个例子一样使用在主键字段上。

其中,在这个例子中,name 属性“PK_SEQ”标示了这个生成器,也就是说这个生成器的名字是 PK_SEQ。

这个 Table 属性标示了用哪个表来存贮生成的主键,在这个例子中,用“ SEQUENCE_TABLE”来存储主键,数据库中有对应的 SEQUENCE_TABLE 表。

其中pkColumnName 属性用来指定的是生成器那个表中的主键,也就是 SEQUENCE_TABLE 这个表的主键的名字。

数据库主键生成策略研究

数据库主键生成策略研究

数据库主键生成策略研究数据库主键生成策略研究一、数据库主键简介数据库视图是非常重要的,因为它们是唯一标识数据库中单条记录和表中每个列之间关系的一个唯一标识符。

一个有效的数据库主键可以有效地实现如下几个目标:1、索引:使用一个唯一的主键能够保证可以通过其他列的值轻松地检索特定行。

2、外键:如果使用正确的外键,可以提供数据库之间的连接。

3、可伸缩性:设计一个有效的主键,能够让数据库支持更大数据量,更高的数据处理速度。

二、数据库主键设计策略正确的数据库主键设计策略有助于数据库的可伸缩性和实现标准,同时还可以确保数据的完整性和安全性。

下面介绍数据库主键设计策略:1、确保主键的唯一性:在定义主键的时候,应该确保所有记录都具有唯一的主键值。

2、考虑可伸缩性:在定义主键时,应该考虑到可伸缩性,以便数据库可以存储更多数据。

3、使用RDBMS提供的机制:应尽量使用数据库系统提供的主键机制,比如使用计数器列或13位编码列,这样可以避免因手动添加主键而导致的潜在负面影响。

4、尽量减少表格的列:应尽可能减少表格的列,这可以缩减复杂性,提高系统的响应时间。

5、考虑索引的使用:应考虑使用索引对数据进行排序和查询,这样可以降低查询的开销。

6、考虑外键的使用:应考虑使用外键,以便通过一张表的 uniqID可以在另一张表中查询数据。

7、收集应用信息:应该了解应用程序的需求,应考虑使用数据库索引来提高系统性能。

三、数据库主键生成策略数据库主键生成策略是一个重要的设计战略,它可以有效地确保数据库的可伸缩性,以及数据的完整性。

下面介绍通常使用的几种策略: 1、序列:序列是生成唯一ID号最常用的方法,根据设定的初始值和步长,可以自增长生成一个唯一的ID号,可以结合使用索引加快查询效率。

2、GUID:GUID是一种16位的全局唯一标识符。

它有128位,可以分为版本、组织机构、随机数等部分。

它比UUID更具安全性,可以有效地避免因相同ID而发生的冲突。

MySQL中的主键生成和自增ID策略

MySQL中的主键生成和自增ID策略

MySQL中的主键生成和自增ID策略引言:MySQL是当今最流行的关系型数据库管理系统之一,广泛应用于各种类型的应用程序中。

在实际的数据库设计中,主键是一个关键的概念,用于唯一标识数据库表中的每一行数据。

主键的生成和自增ID策略是数据库设计中非常重要的一部分。

1. 主键的作用和定义在数据库设计中,主键用于唯一标识数据库表中的每一行数据。

它具有以下特点:1. 主键必须是唯一的,即每一行数据都必须有一个不同于其他行的主键值。

2. 主键不能为空,即每一行数据都必须有一个对应的主键值。

3. 主键的值不能重复,即不允许有两行数据具有相同的主键值。

主键的定义可以通过在建表语句中使用PRIMARY KEY关键字来实现。

例如:```CREATE TABLE students (id INT PRIMARY KEY,name VARCHAR(50),age INT);```上述例子中,id字段被指定为主键,它的值将用于唯一标识students表中的每一行数据。

2. 主键的生成策略主键的生成策略可以分为两大类:手动指定和自动增长。

下面将分别介绍这两种策略的优缺点和适用场景。

2.1 手动指定主键手动指定主键需要在向表中插入数据时,显式地指定主键的值。

这种方式的优点是可以灵活地控制主键的值,适用于主键值与业务逻辑紧密关联的场景。

然而,手动指定主键的缺点是容易出现主键冲突的问题,需要开发人员时刻保证主键值的唯一性。

2.2 自动增长主键自动增长主键是指在向表中插入数据时,数据库系统自动为主键字段生成一个唯一的值。

这种方式的优点是方便快捷,并且能够保证主键的唯一性。

对于需要大量插入数据的场景,自动增长主键是一个较为理想的选择。

然而,自动增长主键的缺点是在分布式系统中可能存在一些问题,例如主键的不连续性和跳过值。

3. MySQL中的自增ID策略MySQL提供了一个名为AUTO_INCREMENT的特性,可以通过它来实现自动增长主键。

mybatis映射规则

mybatis映射规则

mybatis映射规则MyBatis映射规则提供了一种将数据库表映射到Java对象的方法,简化了在Java代码中操作数据库的过程。

下面将介绍一些常见的MyBatis映射规则。

1. 表名映射:MyBatis默认将驼峰式命名的Java对象映射到下划线分隔的数据库表名。

例如,Java对象名为UserInfo,对应的数据库表名为user_info。

2. 字段映射:MyBatis通过在Java对象的属性上使用注解或XML配置将字段与数据库表中的列进行映射。

常用的映射注解有@Id、@Column、@Transient等。

3. 主键映射:使用注解@Id或XML配置指定Java对象的属性作为主键。

MyBatis支持自增和非自增的主键,并可自定义生成主键的策略。

4. 关联关系映射:MyBatis支持一对一、一对多和多对多等关联关系的映射。

通过使用@One、@Many和@ManyToMany注解或XML配置,可以将关联关系映射到数据库表中。

5. 延迟加载与一次性加载:MyBatis支持延迟加载,即只有在实际使用关联对象时才从数据库加载数据。

一次性加载是指在查询主对象的同时,将关联对象一起加载。

6. 动态SQL:MyBatis提供了强大的动态SQL功能,可以根据不同的条件动态生成SQL语句。

通过使用<if>、<choose>、<when>和<otherwise>等标签,可以实现灵活的查询条件拼接。

总之,MyBatis映射规则允许开发者通过简单的配置和注解,实现Java对象与数据库表之间的映射关系,提高了数据库操作的效率和开发效率。

熟悉并合理运用这些映射规则,能够更好地利用MyBatis进行数据库操作。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

并发自定义主键生成策略在项目开发中,我遇到一个需求.就是要生成自定义的主键.主键的格式为:前缀+ 当天日期+ 自增长序列号自增长序列号: 这个序列号是每天从0或1开始自增长的在了解完需要之后,我做了简单的分析之后就马上进入编码模式,这里我使用的是mysql数据,全部代码已经上传到github,有需要的同学自行下载目录结构(项目管理工具用的maven)srcmainjavacom.kco.bean.SequenceNumberBean com.kco.dao.SequenceNumberDao com.kco.Enum.SequenceNumberEnum com.kco.service.SequenceNumberService com.kco.service.SequenceNumberServiceImpl resourcesMETA-INF/mybatis/mapper/SequenceNumberDao.xml META-INF/mybatis/sql-map-config.xml META-INF/spring/spring-base-jdbc.xml log4j.properties testjavacom.kco.TestSequenceNumberService pom.xml编码1, 首先创建一个maven工程,配置一下jar依赖pom.xml<!--?xml version="1.0" encoding="UTF-8"?--><project xmlns="/POM/4.0.0" xmlns:xsi="/2001/XMLSchema-instance"xsi:schemalocation="/POM/4.0.0/xsd/maven-4.0.0.xsd"><modelversion>4.0.0</modelversion><groupid>com.kco</groupid>mytestcode</artifactid><version>1.0-SNAPSHOT</version><dependencies><dependency><groupid>mons</groupid>commons-lang3</artifactid><version>3.3.2</version></dependency><dependency><groupid>mons</groupid>commons-io</artifactid><version>1.3.2</version></dependency><dependency><groupid>mons</groupid>commons-collections4</artifactid><version>4.1</version></dependency><dependency><groupid>mons</groupid>commons-dbcp2</artifactid><version>2.0.1</version></dependency><dependency><groupid>mons</groupid>commons-pool2</artifactid><version>2.3</version></dependency><!-- test --><dependency><groupid>junit</groupid>junit</artifactid><version>4.10</version><scope>test</scope></dependency><dependency><groupid>org.springframework</groupid>spring-test</artifactid><scope>test</scope><version>4.2.3.RELEASE</version></dependency><dependency><groupid>mysql</groupid>mysql-connector-java</artifactid><version>5.1.12</version><scope>runtime</scope></dependency><dependency><groupid>org.mybatis</groupid>mybatis</artifactid><version>3.2.7</version></dependency><!-- spring --><dependency><groupid>org.springframework</groupid>spring-context</artifactid><version>4.2.3.RELEASE</version></dependency><dependency><groupid>org.springframework</groupid>spring-context-support</artifactid><version>4.2.3.RELEASE</version></dependency><dependency><groupid>org.springframework</groupid>spring-tx</artifactid><version>4.2.3.RELEASE</version></dependency><dependency><groupid>org.springframework</groupid>spring-jdbc</artifactid><version>4.2.3.RELEASE</version></dependency><dependency><groupid>org.springframework</groupid>spring-beans</artifactid><version>4.2.3.RELEASE</version></dependency><dependency><groupid>org.springframework</groupid>spring-aop</artifactid><version>4.2.3.RELEASE</version></dependency><!-- aop --><dependency><groupid>org.aspectj</groupid>aspectjrt</artifactid><version>1.8.8</version></dependency><dependency><groupid>org.aspectj</groupid>aspectjtools</artifactid><version>1.8.8</version></dependency><dependency><groupid>org.aspectj</groupid>aspectjweaver</artifactid><version>1.8.8</version><!--spring mybatis 整合--><dependency><groupid>org.mybatis</groupid>mybatis-spring</artifactid><version>1.2.2</version></dependency><dependency><groupid>log4j</groupid><artifactid>log4j</artifactid><version>1.2.17</version></dependency><dependency><groupid>org.slf4j</groupid><artifactid>slf4j-log4j12</artifactid><version>1.7.13</version></dependency><dependency><groupid>org.slf4j</groupid><artifactid>slf4j-api</artifactid><version>1.7.13</version></dependency><dependency><groupid>org.slf4j</groupid><artifactid>jcl-over-slf4j</artifactid><scope>runtime</scope><version>1.7.13</version></dependency></dependencies><build><plugins><plugin><groupid>org.apache.maven.plugins</groupid><artifactid>maven-compiler-plugin</artifactid><version>3.5.1</version><configuration><source>1.8<target>1.8</target><encoding>UTF-8</encoding></configuration><source><source><source><source></plugin><source><source><source><source><source><source><source><source><source><sour ce><groupid>org.apache.maven.plugins</groupid><artifactid>maven-resources-plugin</artifactid><version>2.6</version><configuration><nonfilteredfileextensions><nonfilteredfileextension>tbl</nonfilteredfileextension></nonfilteredfileextensions></configuration></plugin></plugins><source><source><source><source><source><source><source><source><source><sou rce><source><source><source><source><source><source><source><source><source><source></build><source><source><source><source><source><source><source><source><source><sourc e><source><source><source><source><source><source><source><source><source><source><so urce><source><source><source><source><source><source><source><source><source><source> <source><source><source><source></project>2., 初始化数据库<!--?xml version="1.0" encoding="UTF-8"?--><beans xmlns="/schema/beans" xmlns:aop="/schema/aop"xmlns:context="/schema/context"xmlns:tx="/schema/tx"xmlns:xsi="/2001/XMLSchema-instance"xsi:schemalocation="/schema/beans/schema/beans/spring-beans.xsd/schema/context/schema/context/spring-context.xsd/schema/aop/schema/aop/spring-aop.xsd/schema/tx/schema/tx/spring-tx.xsd"><context:annotation-config><context:component-scan base-package="com.kco"><!-- 配置config的数据库--><bean class="mons.dbcp2.BasicDataSource" id="dataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"><property name="url" value="jdbc:mysql:///test?useUnicode=yes&characterEncoding=utf8&allowMultiQueries=true"><property name="username" value="root"><property name="password" value="123456"><property name="minIdle" value="10"><property name="maxIdle" value="10"><property name="maxWaitMillis" value="-1"><property name="maxTotal" value="10"></property></property></property></property></property></property></property></property> </bean><!-- spring和MyBatis完美整合--><bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory"> <property name="dataSource" ref="dataSource"><property name="configLocation" value="classpath:META-INF/mybatis/sql-map-config.xml"><property name="mapperLocations" value="classpath*:META-INF/mybatis/mapper/*Dao.xml"></property></property></property></bean><bean class="org.mybatis.spring.mapper.MapperScannerConfigurer" id="mapperScannerConfigurer"><property name="annotationClass" value="org.springframework.stereotype.Repository"><property name="basePackage" value="com.kco.dao"></property></property></bean><bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager"><property name="dataSource" ref="dataSource"></property></bean><tx:advice id="txAdvice"><tx:attributes><tx:method name="*" propagation="REQUIRED" rollback-for="ng.Throwable"></tx:method></tx:attributes></tx:advice></aop:advisor></aop:config></context:component-scan></context:annotation-config></beans>4, 配置mybatis sql-map-config.xml?<!--?xml version="1.0" encoding="UTF-8" ?--><configuration><properties><property name="dialect" value="mysql"><property name="pageSqlId" value=".*Page$"></property></property></properties><settings><setting name="cacheEnabled" value="false"><setting name="lazyLoadingEnabled" value="false"></setting></setting></settings></configuration>5, 因为要生成自定义主键一般在开发阶段就已经能确定,所以我这里使用了一个枚举类来管理所有的自定义主键com.kco.Enum.SequenceNumberEnumpackage com.kco.Enum;import com.kco.bean.SequenceNumberBean;/*** com.kco.Enum* Created by swlv on 2016/10/25.*/public enum SequenceNumberEnum {GD(new SequenceNumberBean("GD","工单主键生成策略", 1, 1, 8));private SequenceNumberBean sequenceNumberBean;SequenceNumberEnum(SequenceNumberBean sequenceNumberBean){this.sequenceNumberBean = sequenceNumberBean;}public SequenceNumberBean getSequenceNumberBean() {return sequenceNumberBean;}}6, 数据库PUB_SEQUENCE_NUMBER对应的bean类com.kco.bean.SequenceNumberBean?package com.kco.bean;/*** 主键生成策略的bean* com.kco.bean* Created by swlv on 2016/10/25.*/public class SequenceNumberBean {private String prefix;private String name;private String today;private int minNum;private int currentNum;private int numLength;public String getPrefix() {return prefix;}public void setPrefix(String prefix) {this.prefix = prefix;}public String getName() {return name;}public void setName(String name) { = name;}public String getToday() {return today;}public void setToday(String today) {this.today = today;}public int getMinNum() {return minNum;}public void setMinNum(int minNum) { this.minNum = minNum;}public int getCurrentNum() {return currentNum;}public void setCurrentNum(int currentNum) {this.currentNum = currentNum;}public int getNumLength() {return numLength;}public void setNumLength(int numLength) {this.numLength = numLength;}public SequenceNumberBean() {}public SequenceNumberBean(String prefix, String name, int minNum, int currentNum, int numLength) {this.prefix = prefix; = name;this.minNum = minNum;this.currentNum = currentNum;this.numLength = numLength;}@Overridepublic String toString() {return "SequenceNumberBean{" +"prefix='" + prefix + '\'' +", name='" + name + '\'' +", today='" + today + '\'' +", minNum=" + minNum +", currentNum=" + currentNum +", numLength=" + numLength +'}';}}7, 服务层的接口类以及实现com.kco.service.SequenceNumberServicepackage com.kco.service;import com.kco.Enum.SequenceNumberEnum;/*** com.kco.service* Created by swlv on 2016/10/25.*/public interface SequenceNumberService {/*** 生成一个主键* @param sequenceNumberEnum 主键生成类型* @return 返回一个生成的主键*/String newSequenceNumber(SequenceNumberEnum sequenceNumberEnum);}com.kco.service.SequenceNumberServiceImplpackage com.kco.service;import com.kco.Enum.SequenceNumberEnum;import com.kco.bean.SequenceNumberBean;import com.kco.dao.SequenceNumberDao;import org.springframework.stereotype.Service;import javax.annotation.Resource;/*** com.kco.service* Created by swlv on 2016/10/25.*/@Servicepublic class SequenceNumberServiceImpl implements SequenceNumberService{ @Resourceprivate SequenceNumberDao sequenceNumberDao;@Overridepublic synchronized String newSequenceNumber(SequenceNumberEnum sequenceNumberEnum) {if (sequenceNumberEnum == null){return null;}try {SequenceNumberBean sequenceNumberBean = sequenceNumberDao.newSequenceNumber(sequenceNumberEnum.getSequenceNumberBean() .getPrefix());Thread.sleep(100);if (sequenceNumberBean == null){sequenceNumberBean = sequenceNumberEnum.getSequenceNumberBean();sequenceNumberBean.setToday(sequenceNumberDao.getToday());}Thread.sleep(100);sequenceNumberDao.updateSequenceNumber(sequenceNumberBean);Thread.sleep(100);return String.format("%s%6s%08d",sequenceNumberBean.getPrefix(),sequenceNumberBean.getToday(), CurrentNum());} catch (InterruptedException e) {e.printStackTrace();return "";}}}8, 数据库dao层接口com.kco.dao.SequenceNumberDao?package com.kco.dao;import com.kco.bean.SequenceNumberBean;import org.apache.ibatis.annotations.Param;import org.springframework.stereotype.Repository;/*** com.kco.dao* Created by swlv on 2016/10/25.*/@Repositorypublic interface SequenceNumberDao {/*** 根据前缀生成一个序列号信息* @param prefix 前缀* @return 新的序列号信息*/SequenceNumberBean newSequenceNumber(@Param("prefix") String prefix);/*** 将生成的序列号信息更新到数据库中* @param sequenceNumberBean 需要更新信息*/void updateSequenceNumber(@Param("bean") SequenceNumberBean sequenceNumberBean);/*** 获取数据库当天日期* @return*/String getToday();}9., dao层mybatis的实现SequenceNumberDao.xml<!--?xml version="1.0" encoding="UTF-8"?--><mapper namespace="com.kco.dao.SequenceNumberDao"><resultmap id="sequenceNumberResultMap" type="com.kco.bean.SequenceNumberBean"> <id column="prefix" property="prefix"><result column="name" property="name"><result column="today" property="today"><result column="minNum" property="minNum"><result column="currentNum" property="currentNum"><result column="numLength" property="numLength"></result></result></result></result></result></id></resultmap><select id="newSequenceNumber" resultmap="sequenceNumberResultMap"> SELECT prefix,name,today,minNum,currentNum + 1 as currentNum,numLengthFROM PUB_SEQUENCE_NUMBERWHERE prefix = #{prefix}AND today = DATE_FORMAT(CURRENT_DATE(),'%Y%m%d')</select><update id="updateSequenceNumber" parametertype="com.kco.bean.SequenceNumberBean">REPLACE INTO PUB_SEQUENCE_NUMBER(prefix,name,today,minNum,currentNum,numLength)VALUES(#{bean.prefix},#{},DATE_FORMAT(CURRENT_DATE(),'%Y%m%d'),#{bean.minNum},#{bean.currentNum},#{bean.numLength})</update><select id="getToday" resulttype="string">SELECT DATE_FORMAT(CURRENT_DATE(),'%Y%m%d')</select></mapper>10, 编写测试代码(模拟并发测试) com.kco.TestSequenceNumberServicepackage com.kco;import com.kco.Enum.SequenceNumberEnum;import com.kco.service.SequenceNumberService;import mons.collections4.CollectionUtils;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;import java.util.ArrayList;import java.util.Collections;import java.util.List;import java.util.Queue;import java.util.concurrent.ArrayBlockingQueue;/*** com.cmbchina.base.service* Created by swlv on 2016/10/25.*/public class TestSequenceNumberService {public static void main(String[] args) throws InterruptedException {ApplicationContext applicationContext = new ClassPathXmlApplicationContext("META-INF/spring/spring-base-jdbc.xml");SequenceNumberService sequenceNumberService = applicationContext.getBean(SequenceNumberService.class);Queue<string> queue = new ArrayBlockingQueue<string>(200);List<thread> list = new ArrayList<>();for (int i = 0;i < 200; i ++){list.add(new Thread(()->{String key = sequenceNumberService.newSequenceNumber(SequenceNumberEnum.GD);queue.add(key);}));}for (Thread thread : list){thread.start();}while (queue.size() != 200);System.out.println(queue);}}</thread></string></string>11, 至此整个项目就已经搭建完成细节解析在com.kco.service.SequenceNumberServiceImpl#newSequenceNumber的实现中在更新当天日期字符串时,使用的是sequenceNumberBean.setToday(sequenceNumberDao.getToday());这是因为服务器的时间跟数据库的时间有可能不一致,这里以数据库的时间为准测试1, 查询数据库的数据SELECT count(1) FROM PUB_SEQUENCE_NUMBER结果:count(1)2, 清空数据后,再运行com.kco.TestSequenceNumberService#main3, 然后查一下数据库SELECT * FROM PUB_SEQUENCE_NUMBERprefix NAME today minNum currentNum numLengthGD 工单主键生成策略20161026 1 176 8问题出现了,明明我是启动了200个线程在跑,怎么才生成176个主键,检查代码也没有问题啊,newSequenceNumber也加了关键字synchronized.先把这个问题放一下,来回忆一下synchronized的用法,synchronized是在线程池在访问共同变量时使其加锁,达到互斥的效果共同变量?共同变量?咦,好像有什么不对劲的地方.newSequenceNumber使用的都是局部变量,没有共同变量啊!不对?是有共同变量,那个共同变量就是数据库的记录除此之外使用synchronized还有一个问题,这个项目是web项目的一部分,一般在部署web项目都是有几个单边,即在不同的PC部署几个一模一样的代码,再通过集群访问的方式随机访问其中一台.那么这几个应用其实就是多进程程序,都不再一个JVM中执行,加synchronized来保证互斥,是不够的.4, 既然知道是数据库同步出现了问题,那么怎么保证数据库同步呢?上网搜索一下,发现数据库有悲观锁和乐观锁解决方法就是在Dao层的实现<select id="newSequenceNumber" resultmap="sequenceNumberResultMap">SELECT prefix,name,today,minNum,currentNum + 1 as currentNum,numLengthFROM PUB_SEQUENCE_NUMBERWHERE prefix = #{prefix}AND today = DATE_FORMAT(CURRENT_DATE(),'%Y%m%d')</select>改为?<select id="newSequenceNumber" resultmap="sequenceNumberResultMap">SELECT prefix,name,today,minNum,currentNum + 1 as currentNum,numLengthFROM PUB_SEQUENCE_NUMBERWHERE prefix = #{prefix}AND today = DATE_FORMAT(CURRENT_DATE(),'%Y%m%d') for UPDATE </select>5, 然后再重新查一下数据库SELECT * FROM PUB_SEQUENCE_NUMBERprefix NAME today minNum currentNum numLengthGD 工单主键生成策略20161026 1 200 8发现问题解决了,这里是用到了数据库锁表的机制来实现同步.以后有机会再学一下数据库同步,悲观锁和乐观锁.因为这个我还不太熟悉,这里就不卖弄了.6., 修改一下com.kco.Enum.SequenceNumberEnum 再增加一个自定义主键GDD(new SequenceNumberBean("GDD","工单主键生成策略", 1, 1, 8))7., 清空数据,然后运行com.kco.TestSequenceNumberService#main8, 修改com.kco.TestSequenceNumberService2#main将String key = sequenceNumberService.newSequenceNumber(SequenceNumberEnum.GD);改为String key = sequenceNumberService.newSequenceNumber(SequenceNumberEnum.GDD);再次运行com.kco.TestSequenceNumberService#main, 这是用来模拟两个程序同时访问数据的情况9, 查询数据SELECT * FROM PUB_SEQUENCE_NUMBERprefix NAME today minNum currentNum numLengthGD 工单主键生成策略20161026 1 200 8GDD 工单主键生成策略20161026 1 200 8数据正常10., 清空数据,连续运行两次com.kco.TestSequenceNumberService2#main模拟多进程对生成同一个自定义主键11, 查询结果SELECT * FROM PUB_SEQUENCE_NUMBERprefix NAME today minNum currentNum numLengthGDD 工单主键生成策略20161026 1 400 8至此, 全部搞定遗留问题。

相关文档
最新文档