PostgreSQL/JPA - Import functions in import.sql file?

独自空忆成欢 提交于 2019-12-23 12:11:15

问题


I'm trying to define some PostgreSQL functions and triggers in my JPA import.sql file. I'm using Hibernate 5.x as my underlying JPA provider. Since my import.sql file has commands that are multiple lines, I've got this property set in my persistence.xml file:

<property name="hibernate.hbm2ddl.import_files_sql_extractor" value="org.hibernate.tool.hbm2ddl.MultipleLinesSqlCommandExtractor" />

From there, I'm trying to define my functions in import.sql. They look something like this:

DROP FUNCTION IF EXISTS update_total_feedback_count() CASCADE;
CREATE FUNCTION update_total_feedback_count() RETURNS TRIGGER AS
$$
DECLARE
  application_version_id BIGINT := 0;
  app_version_metadata_id BIGINT:= 0;
  application_id BIGINT := 0;
  app_metadata_id BIGINT := 0;
BEGIN
    IF (TG_OP = 'INSERT') THEN
        SELECT INTO application_version_id tbl_application_version.id
            FROM tbl_application_version
            INNER JOIN tbl_feedback ON tbl_feedback.application_version_id = tbl_application_version.id
            WHERE tbl_feedback.id = NEW.id;

        SELECT INTO app_version_metadata_id tbl_application_version.application_version_metadata_id
            FROM tbl_application_version
            WHERE id = application_version_id;

        SELECT INTO app_metadata_id registered_application_metadata_id
            FROM tbl_registered_application
            INNER JOIN tbl_application_version ON tbl_application_version.registered_application_id = tbl_registered_application.id
            WHERE tbl_application_version.id = application_version_id;

        UPDATE tbl_registered_application_metadata SET feedbackcount = (feedbackcount + 1), lastfeedbackdate = NEW.createddate WHERE id = app_metadata_id;
        UPDATE tbl_application_version_metadata SET feedbackcount = (feedbackcount + 1), lastfeedbackdate = NEW.createddate WHERE id = app_version_metadata_id;

        RETURN NEW;
    ELSIF (TG_OP = 'DELETE') THEN
        -- IMPLEMENT THIS TRIGGER

        RETURN NULL;
    END IF;
END;
$$
LANGUAGE 'plpgsql';
ALTER FUNCTION update_total_feedback_count() OWNER TO feedback_tracker;

However, when I deploy my WAR file, I get an error saying something like this:

Unterminated dollar quote started at position 65 in SQL CREATE FUNCTION my_function() RETURNS TRIGGER AS $$

So, clearly it's dying on the $$ in my function declaration. Is there a way around this? Should I be declaring my function/trigger differently? Is there a property I can set in my persistence.xml file that will get around this?


回答1:


the problem with hibernate's default SqlStatementParser implementation, which is used in multiline sql command extractor.

if you look at grammar definition hibernate-core/src/main/antlr/sql-stmt.g there is definition of Statement End:

STMT_END
    : ';' ( '\t' | ' ' | '\r' | '\n' )*
    ;

NOT_STMT_END
    : ~( ';' )
    ;

This tells that statement end is semicolon symbol followed by "Space" "tab" "carret return" or "new line" symbols.

THUS: DEFAULT IMPLEMENTATION IN HIBERNATE DOESN'T SUPPORT DOLLAR QUOTING.

If you don't want to implement custom hibernate's parser you can rewrite all functions without dollar quoting, using simple ' quoting. But you will need to carefully escape ' chars.

UPDATE: you can create your custom ImportSqlCommandExtractor. For example: separate your commands with --****** (6 star symbols in comment, just to make your file proper SQL file, but with custom command separation in comments, or choose any insane combination, which you like) and then split those in simple implementation

public class ImportSqlCE implements ImportSqlCommandExtractor {

    private static final Logger log = LoggerFactory.getLogger(ImportSqlCE.class);

    @Override
    public String[] extractCommands(Reader reader) {
        try {
            String allCommands = IOUtils.toString(reader);

            return allCommands.split("--******");
        } catch (IOException e) {
            log.error("error reading import commands", e);
            log.info("sengind default empty command set");
            return new String[0];
        }
    }
}

and then configure hibernate to use it <property name="hibernate.hbm2ddl.import_files_sql_extractor" value="example.ImportSqlCE" />

with this your import.sql will support dollar quoting (i.e. it will simply ignore any sql awareness of what is happening.)



来源:https://stackoverflow.com/questions/50237165/postgresql-jpa-import-functions-in-import-sql-file

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!