• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

torand / FasterSQL / 15071216480

16 May 2025 02:49PM UTC coverage: 69.877% (+2.4%) from 67.475%
15071216480

push

github

web-flow
Merge pull request #30 from torand/access-support

Access support

229 of 414 branches covered (55.31%)

Branch coverage included in aggregate %.

105 of 152 new or added lines in 26 files covered. (69.08%)

3 existing lines in 3 files now uncovered.

1193 of 1621 relevant lines covered (73.6%)

3.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.33
/src/main/java/io/github/torand/fastersql/statement/InsertBatchStatement.java
1
/*
2
 * Copyright (c) 2024-2025 Tore Eide Andersen
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
package io.github.torand.fastersql.statement;
17

18
import io.github.torand.fastersql.Column;
19
import io.github.torand.fastersql.Context;
20
import io.github.torand.fastersql.Table;
21
import io.github.torand.fastersql.dialect.AnsiIsoDialect;
22
import io.github.torand.fastersql.dialect.OracleDialect;
23

24
import java.util.Collection;
25
import java.util.List;
26
import java.util.Optional;
27
import java.util.function.Function;
28
import java.util.stream.Stream;
29

30
import static io.github.torand.fastersql.Command.INSERT;
31
import static io.github.torand.fastersql.util.collection.CollectionHelper.asList;
32
import static io.github.torand.fastersql.util.collection.CollectionHelper.concat;
33
import static io.github.torand.fastersql.util.collection.CollectionHelper.isEmpty;
34
import static io.github.torand.fastersql.util.collection.CollectionHelper.streamSafely;
35
import static io.github.torand.fastersql.util.contract.Requires.requireNonEmpty;
36
import static java.util.Objects.isNull;
37
import static java.util.Objects.requireNonNull;
38
import static java.util.stream.Collectors.joining;
39

40
public class InsertBatchStatement<T> implements PreparableStatement {
41
    private final Table<?> table;
42
    private final List<ColumnValueExtractor<? super T>> columnValueExtractors;
43
    private final List<? extends T> entities;
44

45
    InsertBatchStatement(Collection<? extends T> entities, Table<?> table, Collection<ColumnValueExtractor<? super T>> columnValueExtractors) {
2✔
46
        this.entities = asList(requireNonEmpty(entities, "No entities specified"));
8✔
47
        this.table = requireNonNull(table, "No table specified");
6✔
48
        this.columnValueExtractors = asList(columnValueExtractors);
4✔
49
    }
1✔
50

51
    public InsertBatchStatement<T> value(Column column, Function<? super T, Object> valueExtractor) {
52
        requireNonNull(column, "No column specified");
4✔
53
        requireNonNull(valueExtractor, "No value extractor specified");
4✔
54

55
        List<ColumnValueExtractor<? super T>> concatenated = concat(columnValueExtractors, new ColumnValueExtractor<>(column, valueExtractor));
14✔
56
        return new InsertBatchStatement<>(entities, table, concatenated);
9✔
57
    }
58

59
    @Override
60
    public String sql(Context context) {
61
        final Context localContext = context.withCommand(INSERT);
4✔
62
        validate();
2✔
63

64
        if (context.getDialect() instanceof OracleDialect) {
4✔
65

66
            // INSERT ALL
67
            //   INTO t (col1, col2, col3) VALUES ('val1_1', 'val1_2', 'val1_3')
68
            //   INTO t (col1, col2, col3) VALUES ('val2_1', 'val2_2', 'val2_3')
69
            //   INTO t (col1, col2, col3) VALUES ('val3_1', 'val3_2', 'val3_3')
70
            // SELECT 1 FROM DUAL;
71

72
            StringBuilder sb = new StringBuilder();
4✔
73
            sb.append("insert all");
4✔
74
            entities().forEach(e -> {
7✔
75
                sb.append(" into ").append(table.sql(localContext));
9✔
76
                sb.append(" (");
4✔
77
                sb.append(columnValueExtractors().map(cve -> cve.column().sql(localContext)).collect(joining(", ")));
17✔
78
                sb.append(") values (");
4✔
79
                sb.append(columnValueExtractors().map(cve -> cve.valueSql(localContext, e)).collect(joining(", ")));
18✔
80
                sb.append(")");
4✔
81
            });
1✔
82

83
            sb.append(" select 1 from DUAL");
4✔
84

85
            return sb.toString();
3✔
86
        } else {
87

88
            // INSERT INTO t
89
            //   (col1, col2, col3)
90
            // VALUES
91
            //   ('val1_1', 'val1_2', 'val1_3'),
92
            //   ('val2_1', 'val2_2', 'val2_3'),
93
            //   ('val3_1', 'val3_2', 'val3_3');
94

95
            StringBuilder sb = new StringBuilder();
4✔
96
            sb.append("insert into ").append(table.sql(context));
9✔
97
            sb.append(" (");
4✔
98
            sb.append(columnValueExtractors().map(cve -> cve.column().sql(localContext)).collect(joining(", ")));
17✔
99
            sb.append(") values ");
4✔
100
            sb.append(entities().map(e -> "("
12✔
101
                + columnValueExtractors().map(cve -> cve.valueSql(localContext, e)).collect(joining(", "))
15✔
102
                + ")")
103
                .collect(joining(", ")));
3✔
104

105
            return sb.toString();
3✔
106
        }
107
    }
108

109
    private Stream<? extends T> entities() {
110
        return streamSafely(entities);
4✔
111
    }
112

113
    private Stream<ColumnValueExtractor<? super T>> columnValueExtractors() {
114
        return streamSafely(columnValueExtractors);
4✔
115
    }
116

117
    @Override
118
    public Stream<Object> params(Context context) {
119
        return entities()
5✔
120
            .flatMap(e -> columnValueExtractors()
6✔
121
                .map(cve -> cve.valueParam(e))
6✔
122
                .flatMap(Optional::stream));
1✔
123
    }
124

125
    private void validate() {
126
        if (isNull(table)) {
4!
127
            throw new IllegalStateException("No table specified");
×
128
        }
129
        if (isEmpty(columnValueExtractors)) {
4!
130
            throw new IllegalStateException("No values specified");
×
131
        }
132
        validateColumnTableRelations(columnValueExtractors().map(ColumnValueExtractor::column));
6✔
133
    }
1✔
134

135
    private void validateColumnTableRelations(Stream<Column> columns) {
136
        columns
3✔
137
            .filter(c -> !table.name().equals(c.table().name()))
11!
138
            .findFirst()
2✔
139
            .ifPresent(c -> {
1✔
140
                throw new IllegalStateException("Column " + c.name() + " belongs to table " + c.table().name() + ", not the table specified by the INTO clause");
×
141
            });
142
    }
1✔
143

144
    @Override
145
    public String toString() {
NEW
146
        return toString(new AnsiIsoDialect());
×
147
    }
148
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc