java - 而不是 INSERT 触发器被绕过

标签 java sql-server database-trigger

我有一个表,其中有一列名为 tripNumber不应该有重复的。

我知道,我可以更改表格并将该列设为 unique但由于某种原因,我无法更改该表,因为它已经在生产中。因此我编写了以下触发器,它基本上执行相同的操作。

    USE [cst_abc]
GO

/****** Object:  Trigger [dbo].[checkTripNumber]    Script Date: 12/21/2019 18:37:10 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE trigger [dbo].[checkTripNumber] on [dbo].[tripDetails]
instead of insert
as 
begin
if exists(select * from [dbo].[tripDetails] where tripNumber = (select [tripNumber] from inserted i))
RAISERROR ('Trip is already there.',15,0);
else
INSERT INTO [cst_abc].[dbo].[tripDetails]
           ([tripNumber]
           ,[noW]
           ,[EndTime]
           ,[someText]
           ,[totalInput]
           ,[totalOutput]
           ,[Difference]
           ,[start]
           ,[end]
           ,[StartTime]
           ,[EndTime]
           ,[serverSync])
SELECT[tripNumber],[noW],[EndTime]
           ,[someText]
           ,[totalInput]
           ,[totalOutput]
           ,[Difference]
           ,[start]
           ,[end]
           ,[StartTime]
           ,[EndTime]
           ,[serverSync] from inserted i
end
GO

它确实按预期工作。我写了一个小的java代码,它基本上启动一个新线程并尝试插入行。我所做的是首先检查行程是否存在,如果存在,则不执行任何操作,否则插入具有特定 id 的新行。

public static void startThread()
    {
        new Thread(() -> {
            try {
                showTimeInMilli("FuncA");
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }).start();
        new Thread(() -> {
            try {
                showTimeInMilli("FuncB");
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }).start();

    }

public static void showTimeInMilli(String name) throws SQLException
    {
        System.out.println("called from "+name +"Current time is "+System.currentTimeMillis());
        if(checkTripNumber(1))
        {
                System.out.println("called from "+name +" and trip exists.");
        }
        else
        {
            System.out.println("called from "+name +" and inserting new row.");
            SqlUtil.startNewTrip(1,7,"ap1","2019-06-18 07:06:00",5,1576631560);
        }
    }

这里需要注意的是,这个触发器即startTrip可以从多个源触发,并且我发现大多数时候它会同时触发(我存储 epoch 时间,例如从两个源触发,它恰好在 1576934304 处触发)

问题

十分之九的情况符合预期,即它不会添加新行,但有时会添加重复的 tripNumber 。非常感谢任何帮助。

上述 java 代码的理想日志是:

called from FuncACurrent time is 1576933097423
called from FuncBCurrent time is 1576933097423
td before sendig false
called from FuncB and inserting new row.
td before sendig false
called from FuncA and inserting new row.
com.microsoft.sqlserver.jdbc.SQLServerException: Trip is already there.
    at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDatabaseError(SQLServerException.java:217)
    at com.microsoft.sqlserver.jdbc.SQLServerStatement.getNextResult(SQLServerStatement.java:1655)
    at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement.doExecutePreparedStatement(SQLServerPreparedStatement.java:440)
    at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement$PrepStmtExecCmd.doExecute(SQLServerPreparedStatement.java:385)
    at com.microsoft.sqlserver.jdbc.TDSCommand.execute(IOBuffer.java:7505)
    at com.microsoft.sqlserver.jdbc.SQLServerConnection.executeCommand(SQLServerConnection.java:2445)
    at com.microsoft.sqlserver.jdbc.SQLServerStatement.executeCommand(SQLServerStatement.java:191)
    at com.microsoft.sqlserver.jdbc.SQLServerStatement.executeStatement(SQLServerStatement.java:166)
    at com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement.execute(SQLServerPreparedStatement.java:367)
    at com.zaxxer.hikari.pool.ProxyPreparedStatement.execute(ProxyPreparedStatement.java:44)
    at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.execute(HikariProxyPreparedStatement.java)
    at database.SqlUtil.startNewTrip(SqlUtil.java:45)
    at database.Hikari.showTimeInMilli(Hikari.java:122)
    at database.Hikari.lambda$0(Hikari.java:44)
    at java.lang.Thread.run(Unknown Source)
SQL Exception2 com.microsoft.sqlserver.jdbc.SQLServerException: Trip is already there.

意外日志是:

called from FuncACurrent time is 1576933097323
called from FuncBCurrent time is 1576933097323
td before sendig false
called from FuncB and inserting new row.
td before sendig false
called from FuncA and inserting new row.

最佳答案

情况是 INSERT 与检查 tripNumber 存在分开:

INSERT INTO [cst_abc].[dbo].[tripDetails]
           ([tripNumber]
           ,[noW]
           ,[EndTime]
           ,[someText]
           ,[totalInput]
           ,[totalOutput]
           ,[Difference]
           ,[start]
           ,[end]
           ,[StartTime]
           ,[EndTime]
           ,[serverSync])
SELECT[tripNumber],[noW],[EndTime]
           ,[someText]
           ,[totalInput]
           ,[totalOutput]
           ,[Difference]
           ,[start]
           ,[end]
           ,[StartTime]
           ,[EndTime]
           ,[serverSync] 
from inserted i
where NOT EXISTS (SELECT 1 FROM [dbo].[tripDetails] d WHERE i.[tripNumber] = d.[tripNumber]);

无论如何,这种“解决方法”不是一个好的方法,应该引入正常的“UNIQUE”约束。

<小时/>

编辑:

I cant alter the table as it's already in production

如何添加唯一索引(从技术上讲,表没有改变,索引是单独的对象):

CREATE UNIQUE INDEX udx_tripDetails(tripNumber) ON [dbo].[tripDetails](tripNumber)
WITH(ONLINE = ON);

关于java - 而不是 INSERT 触发器被绕过,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59436575/

相关文章:

postgresql - 如果满足特定条件,如何自动更新表

java - 使用 Install4J 创建的 MacOSX 安装程序与标准应用程序包不同

sql - 指定 SELECT * 时出现传输级错误

sql-server - 使用 SQL Profiler 创建几天的跟踪文件

mysql - 插入前触发器中的子字符串、计数和连接?

MySQL触发器在插入后检查是否没有重复然后运行sys_exec udf

java - 如何为 map reducer 作业在 java 中为 hadoop 输入自定义选择列读取

java - 如何编写自定义流函数

java - Hibernate Envers 审计 @Embedded 内有基本类型 throws 无法在获取时将字段设置为空值

sql-server - SQL Server CASE 1 WHEN 1,WHERE 1=1 AND 1=1