gpt4 book ai didi

java - JPA entityManager.merge 将 LocalDateTime 转换为 SQLServer 2012 DATETIME2

转载 作者:塔克拉玛干 更新时间:2023-11-03 03:52:33 25 4
gpt4 key购买 nike

我有一个以 DateTime 列作为主键的表:

USE [idatest]
GO

CREATE TABLE [dbo].[DatesTbl](
[creationDate] [datetime] NOT NULL
CONSTRAINT [PK_DatesTbl] PRIMARY KEY CLUSTERED
(
[creationDate] ASC
))
GO

当我执行 entityManager.merge 时,我得到重复,PK 违规,因为 datetime 为 milisec 保留 3 位数字,但 hibernet 将其转换为 datetime2 ,它为 milisec 保留 7 位数字。在 java 代码中,我使用 LocaDatetime,它包含 10 位毫秒数。

我已经尝试了解释的解决方案 Hibernate MSSQL datetime2 mapping但它不起作用:Java 代码如下所示:pom.xml

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
</parent>

<groupId>com.example</groupId>
<artifactId>spring-jap-test</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>

<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
</dependency>
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<version>7.0.0.jre8</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>

DatesTbl 类

@Entity
@NoArgsConstructor
@AllArgsConstructor
public class DatesTbl {

@Column(columnDefinition = "DATETIME", nullable = false)
@Id
private LocalDateTime creationDate;
}

主类

@EnableTransactionManagement
public class Main {

public static void main(String[] args) {

ApplicationContext context = new AnnotationConfigApplicationContext(Main.class);

EntityManagerFactory entityManagerFactory = context.getBean(EntityManagerFactory.class);
final EntityManager entityManager = entityManagerFactory.createEntityManager();
final LocalDateTime creationDate = LocalDateTime.of(2018, 12, 26, 8, 10, 40, 340);
entityManager.getTransaction().begin();
final DatesTbl datesTbl = entityManager.merge(new DatesTbl(creationDate));
entityManager.getTransaction().commit();

System.out.println("test");
}

@Bean
@Primary
public DataSource getDataSource() {

SQLServerDataSource ds = null;
try {
ds = new SQLServerDataSource();
ds.setServerName("localhost");
ds.setDatabaseName("idatest");
ds.setIntegratedSecurity(true);
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
return ds;
}


@Bean
public JpaVendorAdapter jpaVendorAdapter() {
HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
hibernateJpaVendorAdapter.setShowSql(true);
hibernateJpaVendorAdapter.setGenerateDdl(true);
hibernateJpaVendorAdapter.setDatabase(Database.SQL_SERVER);
return hibernateJpaVendorAdapter;
}


@Bean
public LocalContainerEntityManagerFactoryBean abstractEntityManagerFactoryBean(
JpaVendorAdapter jpaVendorAdapter) {

Properties properties = new Properties();
properties.setProperty(FORMAT_SQL, String.valueOf(true));
properties.setProperty(SHOW_SQL, String.valueOf(true));
properties.setProperty(DIALECT, ModifiedSQLServerDialect.class.getTypeName());
LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean =
new LocalContainerEntityManagerFactoryBean();

localContainerEntityManagerFactoryBean.setDataSource(getDataSource());
localContainerEntityManagerFactoryBean.setJpaVendorAdapter(jpaVendorAdapter);
localContainerEntityManagerFactoryBean.setJpaProperties(properties);
localContainerEntityManagerFactoryBean.setPackagesToScan("enteties");

return localContainerEntityManagerFactoryBean;
}


@Bean
public PlatformTransactionManager platformTransactionManager(EntityManagerFactory emf) {
return new JpaTransactionManager(emf);
}
}


public class ModifiedSQLServerDialect extends SQLServer2012Dialect {


public ModifiedSQLServerDialect () {
super();
registerColumnType(Types.TIMESTAMP, "timestamp");
registerColumnType(Types.DATE, "timestamp");
registerColumnType(Types.TIME, "timestamp");
registerHibernateType(Types.TIMESTAMP, "timestamp");
registerHibernateType(Types.DATE, "timestamp");
registerHibernateType(Types.TIME, "timestamp");
}
}

但我仍然在 SQLServer 分析器中看到:

exec sp_executesql N'select datestbl0_.creationDate as creation1_0_0_ from DatesTbl datestbl0_ where datestbl0_.creationDate=@P0        ',N'@P0 `datetime2`','2018-12-26 08:10:40.0000003'

解决方案有什么问题?

最佳答案

解释

该问题与 mssql-jdbc(版本 4.x 和 6.x)中的问题有关,PreparedStatement.setTimestamp(index, timestamp, calendar) has datatype conversion issues ,它始终将数据类型为 datetime2LocalDateTime 参数发送到 SQL Server(忽略表的列类型)。由于datetime(0.00333sec)和datetime2(100纳秒)的精度不同,并且使用datetime作为PK,导致Hibernate工作错误在这种情况下。

当我们运行主程序时,creationDate 的值为 2018-12-26 08:10:40.000000340 并且该值保存为 2018- 12-26 08:10:40.000 在数据库中作为 Hibernate 在数据库中找不到具有相同键的记录。当我们再次运行主程序时,Hibernate 首先检查是否有具有相同键的记录,使用

'select datestbl0_.creationDate as creation1_0_0_ from DatesTbl datestbl0_ where datestbl0_.creationDate=@P0 ',N'@P0 'datetime2'','2018-12-26 08:10:40.0000003'

SQL Server似乎将表中的datetime值向上转换为datetime2进行比较,没有返回记录。因此 Hibernate 再次插入记录,并导致 Primary Key Violation。

解决方法

正如 Vlad Mihalcea 所建议的,使用 DATETIME 列作为 PK 并不是一个好主意。
但是,假设我们仍然需要 datetime 列作为 PK,以下解决方法应该有效。解决这个问题的关键是让datetimedatetime2比较返回true。为此,我们可以在传递给 DB 之前将 datetime2 值截断/舍入为相应的 datetime 值。以下对主程序的改动在SQL Server 2012 Express上测试无误。

public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(Main.class);

EntityManagerFactory entityManagerFactory = context.getBean(EntityManagerFactory.class);
final EntityManager entityManager = entityManagerFactory.createEntityManager();

LocalDateTime creationDate0 = LocalDateTime.of(2018, 12, 26, 8, 10, 40, 341340340);
LocalDateTime creationDate3 = LocalDateTime.of(2018, 12, 26, 8, 10, 40, 343340340);
LocalDateTime creationDate7 = LocalDateTime.of(2018, 12, 26, 8, 10, 40, 346670340);
LocalDateTime creationDate10 = LocalDateTime.of(2018, 12, 26, 8, 10, 40, 349670340);
entityManager.getTransaction().begin();
final DatesTbl datesTbl0 = entityManager.merge(new DatesTbl(roundNanoSecForDateTime(creationDate0)));
final DatesTbl datesTbl3 = entityManager.merge(new DatesTbl(roundNanoSecForDateTime(creationDate3)));
final DatesTbl datesTbl7 = entityManager.merge(new DatesTbl(roundNanoSecForDateTime(creationDate7)));
final DatesTbl datesTbl10 = entityManager.merge(new DatesTbl(roundNanoSecForDateTime(creationDate10)));
entityManager.getTransaction().commit();
System.out.println("test");
}

private static LocalDateTime roundNanoSecForDateTime(LocalDateTime localDateTime) {
int nanoSec = localDateTime.getNano();
// The rounding is based on following results on SQL server 2012 express
// select cast(cast('2018-12-26 08:10:40.3414999' as datetime2) as datetime);
// 2018-12-26 08:10:40.340
// select cast(cast('2018-12-26 08:10:40.3415000' as datetime2) as datetime);
// select cast(cast('2018-12-26 08:10:40.3444999' as datetime2) as datetime);
// 2018-12-26 08:10:40.343
// select cast(cast('2018-12-26 08:10:40.3445000' as datetime2) as datetime);
// select cast(cast('2018-12-26 08:10:40.3484999' as datetime2) as datetime);
// 2018-12-26 08:10:40.347
// select cast(cast('2018-12-26 08:10:40.3485000' as datetime2) as datetime);
// 2018-12-26 08:10:40.350
int last7DigitOfNano = nanoSec - (nanoSec / 10000000) * 10000000;
int roundedNanoSec = 0;
if (last7DigitOfNano < 1500000) {
roundedNanoSec = nanoSec - last7DigitOfNano;
} else if (last7DigitOfNano < 4500000) {
roundedNanoSec = nanoSec - last7DigitOfNano + 3000000;
} else if (last7DigitOfNano < 8500000) {
roundedNanoSec = nanoSec - last7DigitOfNano + 7000000;
} else {
roundedNanoSec = nanoSec - last7DigitOfNano + 10000000;
}
System.out.println("Before Rounding" + nanoSec);
System.out.println("After Rounding" + roundedNanoSec);
return localDateTime.withNano(roundedNanoSec);
}

引用:
1. DateTime2 vs DateTime in SQL Server
2. Date and Time Data Types and Functions (Transact-SQL)

关于java - JPA entityManager.merge 将 LocalDateTime 转换为 SQLServer 2012 DATETIME2,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53651170/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com