惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Threat Research - Cisco Blogs
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
V
Vulnerabilities – Threatpost
GbyAI
GbyAI
P
Proofpoint News Feed
L
LINUX DO - 热门话题
P
Palo Alto Networks Blog
A
About on SuperTechFans
T
Tenable Blog
M
MIT News - Artificial intelligence
IT之家
IT之家
I
Intezer
D
DataBreaches.Net
爱范儿
爱范儿
T
Threatpost
C
CERT Recently Published Vulnerability Notes
云风的 BLOG
云风的 BLOG
博客园 - 三生石上(FineUI控件)
WordPress大学
WordPress大学
K
Kaspersky official blog
大猫的无限游戏
大猫的无限游戏
A
Arctic Wolf
Y
Y Combinator Blog
Cyberwarzone
Cyberwarzone
酷 壳 – CoolShell
酷 壳 – CoolShell
D
Darknet – Hacking Tools, Hacker News & Cyber Security
H
Help Net Security
Microsoft Security Blog
Microsoft Security Blog
Spread Privacy
Spread Privacy
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
AWS News Blog
AWS News Blog
博客园 - 聂微东
C
Check Point Blog
S
Securelist
有赞技术团队
有赞技术团队
雷峰网
雷峰网
aimingoo的专栏
aimingoo的专栏
Last Week in AI
Last Week in AI
Stack Overflow Blog
Stack Overflow Blog
MongoDB | Blog
MongoDB | Blog
D
Docker
G
GRAHAM CLULEY
T
The Exploit Database - CXSecurity.com
C
Cybersecurity and Infrastructure Security Agency CISA
T
Tailwind CSS Blog
L
Lohrmann on Cybersecurity
G
Google Developers Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
L
LangChain Blog

博客园 - 漫漫人生路总会错几步

一种非常巧妙的设计模式 【流量密码】LVS与nginx对比 【架构升华】:数据库是性能的物理终点 【轻量化交付宣言】:DevOps 的本质是工程化,而非工具化 【微服务】是【必须品】吗? 【JWT】真的好吗? PGSQL 1主2从数据库架构与单节点分3库在三块磁盘理论上限畅想(未测试) 相同的硬件,各个数据库专家比赛畅想 maven 原型项目 mysql9.5安装文档 微信图片批量保存的办法 win平台利用winsw将php-cgi作为系统服务,支持服务的正常启动/停止/重启 利用WinSW将Nginx 作为可正常启动/停止的windows服务 【ubuntu】程序运行时的任务栏图标 跨网段通信实战(支持静态路由表的家用路由) Linux系统Mariadb初始化相关(ubuntu) springboot 整合webservice 相关说明 tomcat 服务版本内存设置 navicat连接mysql8报错
JPA使用pg数据库时,bool字段不能跨库迁移的解决方案
漫漫人生路总会错几步 · 2024-05-13 · via 博客园 - 漫漫人生路总会错几步

首先,大多数人的印象里,hibernate作为一个笨重学习成本高的近乎全自动的框架它的优点就是可以支持很多数据库

但是最近研究发现,java中的boolean类型的字段,在mariadb/mysql 中为bit 0/1, 在sqlserver/oracle中为bit 0/1 和numeric(1,0) check * in (0,1),

然而在PG数据库中却是bool,因为pg就是支持boolean到bool的映射

所以,java中的boolean 在除PG的数据库之外的几个常见数据库中存储的都是 0/1,这就导致了数据迁移时会出现不兼容的问题。

在GPT的帮助下,经研究hibernate的数据库方言发现,可以通过修改官方的方言以达到实现兼容的目的。

相关代码如下,PgDialect和CustomerBooleanJdbcType(适用于Hibernate7+)

import java.sql.Types;

import org.hibernate.boot.model.TypeContributions;
import org.hibernate.dialect.PostgreSQLDialect;
import org.hibernate.service.ServiceRegistry;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class PgDialect extends PostgreSQLDialect {

	public PgDialect() {
		super();
	}

	/**
	 * 这里的方法注释会导致java代码查询的逻辑删除值是true/false
	 * 但取消注释会导致java代码的逻辑删除列定义由原本的smallint改为integer,但逻辑删除时的where deleted=1 是对的
	 * 结论:@SoftDelete注解不遵守CustomerBooleanJdbcType给出的建议,或者说它有自己独立的逻辑
	 */
	@Override
	public int getPreferredSqlTypeCodeForBoolean() {
		return Types.SMALLINT;
	}

	@Override
	protected void registerColumnTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
		super.registerColumnTypes(typeContributions, serviceRegistry);
		typeContributions.contributeJdbcType(CustomerBooleanJdbcType.INSTANCE);
	}
}
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Objects;

import org.hibernate.dialect.Dialect;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.type.SqlTypes;
import org.hibernate.type.descriptor.ValueBinder;
import org.hibernate.type.descriptor.ValueExtractor;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.converter.spi.BasicValueConverter;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.jdbc.BasicBinder;
import org.hibernate.type.descriptor.jdbc.BasicExtractor;
import org.hibernate.type.descriptor.jdbc.BooleanJdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcLiteralFormatter;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcTypeIndicators;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class CustomerBooleanJdbcType extends BooleanJdbcType {

	private CustomerBooleanJdbcType() {
	}

	public static final CustomerBooleanJdbcType INSTANCE = new CustomerBooleanJdbcType();
	private static final long serialVersionUID = 1L;

	@Override
	public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
		return new BasicBinder<>(javaType, this) {
			@Override
			protected void doBindNull(PreparedStatement st, int index, WrapperOptions options) throws SQLException {
				st.setNull(index, options.getPreferredSqlTypeCodeForBoolean());
			}

			@Override
			protected void doBindNull(CallableStatement st, String name, WrapperOptions options) throws SQLException {
				st.setNull(name, options.getPreferredSqlTypeCodeForBoolean());
			}

			@Override
			protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options)
					throws SQLException {
				st.setInt(index, Objects.equals(true, value) ? 1 : 0);
			}

			@Override
			protected void doBind(CallableStatement st, X value, String name, WrapperOptions options)
					throws SQLException {
				st.setInt(name, Objects.equals(true, value) ? 1 : 0);
			}
		};
	}

	@Override
	public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaType<T> javaType) {
		return (SqlAppender appender, T value, Dialect dialect, WrapperOptions wrapperOptions) -> appender
				.append(Objects.equals(value, true) || Objects.equals(value, 1) ? "1" : "0");

	}

	@Override
	public JdbcType resolveIndicatedType(JdbcTypeIndicators indicators, JavaType<?> domainJtd) {
		return this;
	}

	@Override
	public <X> ValueExtractor<X> getExtractor(JavaType<X> javaType) {
		return new BasicExtractor<>(javaType, this) {
			@Override
			protected X doExtract(ResultSet rs, int paramIndex, WrapperOptions options) throws SQLException {
				String label = rs.getMetaData().getColumnName(paramIndex);
				Object o = rs.getObject(paramIndex);
				int result = rs.getInt(paramIndex);
				log.debug("label:{} ,result: {} , o: {}", label, result, o);
				return javaType.wrap(Objects.equals(result, 1), options);
			}

			@Override
			protected X doExtract(CallableStatement statement, int index, WrapperOptions options) throws SQLException {
				return javaType.wrap(Objects.equals(statement.getInt(index), 1), options);
			}

			@Override
			protected X doExtract(CallableStatement statement, String name, WrapperOptions options)
					throws SQLException {
				int result = statement.getInt(name);
				return javaType.wrap(Objects.equals(result, 1), options);
			}
		};
	}

	@Override
	public int getDdlTypeCode() {
		return SqlTypes.SMALLINT;
	}

	@Override
	public int getJdbcTypeCode() {
		return Types.BOOLEAN;
	}

	@Override
	public int getDefaultSqlTypeCode() {
		return Types.BOOLEAN;
	}

	@Override
	public String getCheckCondition(String columnName, JavaType<?> javaType, BasicValueConverter<?, ?> converter,
			Dialect dialect) {
		return columnName + " in (0,1)";
	}

	@Override
	public boolean isBoolean() {
		return false;
	}

	@Override
	public boolean isSmallInteger() {
		return true;
	}

}