如何解决PostgreSQL动态分区和休眠插入无法通过插入触发器返回NULL之前
Postgresql动态分区和休眠插入不能通过触发器工作。
我有一个Graph和Node表。该图通过node_id与Node表具有一对一关系,而node表通过parent_id与自身具有递归关系。每个节点表和图形表都有 tenant_name ,在多租户表中,我想按照https://read.acloud.guru/how-to-partition-dynamically-in-postgresql-ce3acbaef66c
所述使用动态分区进行分区。所以我有
CREATE TABLE IF NOT EXISTS graph
(
id SERIAL NOT NULL,name character varying(255) NOT NULL,opt_lock_version int NOT NUll default 1,type character varying(255) NOT NULL,tenant_name character varying(255) NOT NULL,node_id int,CONSTRAINT graph_pkey PRIMARY KEY (id),CONSTRAINT graph_node_fk FOREIGN KEY (node_id)
REFERENCES node (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,CONSTRAINT graph_node_id_uk UNIQUE (node_id)
);
CREATE TABLE IF NOT EXISTS node
(
id SERIAL NOT NULL,pending_removal boolean,parent_id int,CONSTRAINT node_pkey PRIMARY KEY (id),CONSTRAINT node_parent_id_fk FOREIGN KEY (parent_id)
REFERENCES node (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
);
我已经存储了创建动态表的功能,并在动态创建的动态表中插入了一行(并且有效):
CREATE OR REPLACE FUNCTION graph_insert_function()
RETURNS TRIGGER AS $$
DECLARE
tenant_name TEXT;
node_partition_name TEXT;
graph_partition_name TEXT;
BEGIN
tenant_name := NEW.tenant_name;
graph_partition_name := 'graph_' || tenant_name;
node_partition_name := 'node_' || tenant_name;
IF NOT EXISTS
(SELECT 1
FROM information_schema.tables
WHERE table_name = '|| node_partition_name ||')
THEN
RAISE NOTICE 'A node table partition has been created %',node_partition_name;
EXECUTE format(E'CREATE TABLE IF NOT EXISTS %I ( CHECK ( tenant_name = ''%s'')) INHERITS (node)',node_partition_name,tenant_name) ;
END IF;
IF NOT EXISTS
(SELECT 1
FROM information_schema.tables
WHERE table_name = '|| graph_partition_name ||')
THEN
RAISE NOTICE 'A node table partition has been created %',graph_partition_name;
EXECUTE format(E'CREATE TABLE IF NOT EXISTS %I ( CHECK ( tenant_name = ''%s'')) INHERITS (graph)',graph_partition_name,tenant_name) ;
END IF;
EXECUTE format(E'INSERT INTO %I (id,name,opt_lock_version,type,tenant_name,node_id) VALUES ($1,$2,$3,$4,$5,$6) returning id',graph_partition_name) using NEW.id,NEW.name,NEW.opt_lock_version,NEW.type,NEW.tenant_name,NEW.node_id;
RETURN NULL; // IF I RETURN NEW then it will also insert into main graph table which I do not want.
END
$$
LANGUAGE plpgsql;
问题是现在Graph表中出现了Hibernate Insert,并且由于存储函数返回NULL,所以它返回“ INSERT 0 0”。 Hibernate失败,批处理更新从 update [0]返回意外行计数为1;实际行数:0;预期:1;语句执行。记录已插入到动态表中,但是0 行在主表中受到影响。我不想在主图形表中重复数据(应该为空),只有动态表才会有记录。
如何伪造“ INSERT 0 1”或如何指示已插入数据以使其休眠?
更新:
我能够通过更改休眠内部 org.hibernate.jdbc.Expectations 类来伪造插入内容,该类检查受影响的记录数,并通过threadlocal变量count_zero将其转换为TRUE:现在唯一的问题更新和删除语句没有 tenant_name ,否则它不会对租户进行全表扩展,而不仅仅是分区,因此更新/删除将很慢。
/**
* Holds varIoUs often used {@link Expectation} deFinitions.
*
* @author Steve Ebersole
*/
public class Expectations {
**public static final ThreadLocal<Boolean> count_zero = new ThreadLocal<Boolean>();**
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( Expectations.class );
private static sqlExceptionHelper sqlExceptionHelper = new sqlExceptionHelper( false );
public static final int USUAL_EXPECTED_COUNT = 1;
public static final int USUAL_ParaM_POSITION = 1;
// Base Expectation impls ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public static class BasicExpectation implements Expectation {
private final int expectedRowCount;
protected BasicExpectation(int expectedRowCount) {
this.expectedRowCount = expectedRowCount;
if ( expectedRowCount < 0 ) {
throw new IllegalArgumentException( "Expected row count must be greater than zero" );
}
}
public final void verifyOutcome(int rowCount,PreparedStatement statement,int batchPosition) {
rowCount = determineRowCount( rowCount,statement );
if ( batchPosition < 0 ) {
checkNonBatched( rowCount,statement );
}
else {
checkBatched( rowCount,batchPosition,statement );
}
}
private void checkBatched(int rowCount,int batchPosition,PreparedStatement statement) {
if ( rowCount == -2 ) {
LOG.debugf( "Success of batch update unkNown: %s",batchPosition );
}
else if ( rowCount == -3 ) {
throw new BatchFailedException( "Batch update Failed: " + batchPosition );
}
else {
if ( expectedRowCount > rowCount ) {
**if(count_zero.get() == null || count_zero.get()) {**
throw new StaleStateException(
"Batch update returned unexpected row count from update ["
+ batchPosition + "]; actual row count: " + rowCount
+ "; expected: " + expectedRowCount + "; statement executed: "
+ statement
);
}
}
if ( expectedRowCount < rowCount ) {
**if(count_zero.get() == null || count_zero.get()) {**
String msg = "Batch update returned unexpected row count from update [" +
batchPosition + "]; actual row count: " + rowCount +
"; expected: " + expectedRowCount;
throw new BatchedTooManyRowsAffectedException(msg,expectedRowCount,rowCount,batchPosition);
}
}
}
}
private void checkNonBatched(int rowCount,PreparedStatement statement) {
if ( expectedRowCount > rowCount ) {
**if(count_zero.get() == null || count_zero.get()) {**
throw new StaleStateException(
"Unexpected row count: " + rowCount + "; expected: " + expectedRowCount
+ "; statement executed: " + statement
);
}
}
if ( expectedRowCount < rowCount ) {
**if(count_zero.get() == null || count_zero.get()) {**
String msg = "Unexpected row count: " + rowCount + "; expected: " + expectedRowCount;
throw new TooManyRowsAffectedException(msg,rowCount);
}
}
}
public int prepare(PreparedStatement statement) throws sqlException,HibernateException {
return 0;
}
public boolean canBeBatched() {
return true;
}
protected int determineRowCount(int reportedRowCount,PreparedStatement statement) {
return reportedRowCount;
}
}
public static class BasicParamExpectation extends BasicExpectation {
private final int parameterPosition;
protected BasicParamExpectation(int expectedRowCount,int parameterPosition) {
super( expectedRowCount );
this.parameterPosition = parameterPosition;
}
@Override
public int prepare(PreparedStatement statement) throws sqlException,HibernateException {
toCallableStatement( statement ).registerOutParameter( parameterPosition,Types.NUMERIC );
return 1;
}
@Override
public boolean canBeBatched() {
return false;
}
@Override
protected int determineRowCount(int reportedRowCount,PreparedStatement statement) {
try {
return toCallableStatement( statement ).getInt( parameterPosition );
}
catch (sqlException sqle) {
sqlExceptionHelper.logExceptions( sqle,"Could not extract row counts from CallableStatement" );
throw new GenericJDBCException( "Could not extract row counts from CallableStatement",sqle );
}
}
private CallableStatement toCallableStatement(PreparedStatement statement) {
if ( !CallableStatement.class.isinstance( statement ) ) {
throw new HibernateException(
"BasicParamExpectation operates exclusively on CallableStatements : " + statement.getClass()
);
}
return (CallableStatement) statement;
}
}
// VarIoUs Expectation instances ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public static final Expectation NONE = new Expectation() {
public void verifyOutcome(int rowCount,int batchPosition) {
// explicitly doAfterTransactionCompletion no checking...
}
public int prepare(PreparedStatement statement) {
return 0;
}
public boolean canBeBatched() {
return true;
}
};
public static final Expectation BASIC = new BasicExpectation( USUAL_EXPECTED_COUNT );
public static final Expectation ParaM = new BasicParamExpectation( USUAL_EXPECTED_COUNT,USUAL_ParaM_POSITION );
public static Expectation appropriateExpectation(ExecuteUpdateResultCheckStyle style) {
if ( style == ExecuteUpdateResultCheckStyle.NONE ) {
return NONE;
}
else if ( style == ExecuteUpdateResultCheckStyle.COUNT ) {
return BASIC;
}
else if ( style == ExecuteUpdateResultCheckStyle.ParaM ) {
return ParaM;
}
else {
throw new HibernateException( "unkNown check style : " + style );
}
}
private Expectations() {
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。