-
Notifications
You must be signed in to change notification settings - Fork 1
/
DbOps.java
149 lines (120 loc) · 3.94 KB
/
DbOps.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package com.novarto.sanedbc.core.ops;
import com.novarto.lang.CanBuildFrom;
import fj.F;
import fj.control.db.DB;
import fj.data.Option;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import static com.novarto.lang.CanBuildFrom.fjListCanBuildFrom;
import static fj.data.Option.none;
import static fj.data.Option.some;
/**
* A set of conversions between DB[A] instances.
*/
public final class DbOps
{
private DbOps()
{
throw new UnsupportedOperationException();
}
/**
* Given an existing {@link DB<Iterable<A>>}, converts it to an operation that either returns one result, none at all,
* or throws if there is more than one result in the result set.
*
* This is useful if you are issuing a query by a single primary key. Such a query is never expected to return > 1 results
* (in which case the returned DB will throw upon interpretation), but can return no results (which is expressed in the
* return type)
*/
public static <A> DB<Option<A>> unique(DB<? extends Iterable<A>> op)
{
return op.map(xs ->
{
Iterator<A> it = xs.iterator();
if (!it.hasNext())
{
return none();
}
A result = it.next();
if (it.hasNext())
{
throw new RuntimeException("unique with more than one element");
}
return some(result);
});
}
public static <A> DB<Integer> toChunks(Iterable<A> xs, F<Iterable<A>, DB<Integer>> getOp, int chunkSize)
{
return new DB<Integer>()
{
@Override public Integer run(Connection c) throws SQLException
{
Integer result = 0;
List<List<A>> chunks = chunks(xs, chunkSize);
for (List<A> chunk : chunks)
{
result += getOp.f(chunk).run(c);
}
return result;
}
};
}
private static <A> List<List<A>> chunks(Iterable<A> xs, int chunkSize)
{
if (chunkSize < 1)
{
throw new IllegalArgumentException("chunkSize must be >=1");
}
List<List<A>> result = new ArrayList<>();
List<A> currentList = new ArrayList<>();
result.add(currentList);
int count = 0;
for (A x : xs)
{
if (count >= chunkSize)
{
currentList = new ArrayList<>();
result.add(currentList);
count = 0;
}
currentList.add(x);
count++;
}
List<A> last = result.get(result.size() - 1);
if (last.size() == 0)
{
result.remove(result.size() - 1);
}
return result;
}
/**
* Given an iterable of DB's, convert it to a single DB of iterable. E.g. List[DB[A]] => DB[List[A]].
* Utilizes a CanBuildFrom instance to construct the result iterable
* @param xs the iterable of DB's to convert
* @param cbf the CanBuildFrom instance
* @param <A> the type of elements
* @param <C1> optional intermediate representation, see CanBuildFrom javadoc
* @param <C2> the type of the result iterable, see CanBuildFrom javadoc
* @return A DB[C2[A]]
*/
public static <A, C1 extends Iterable<A>, C2 extends Iterable<A>> DB<C2> sequence(Iterable<DB<A>> xs,
CanBuildFrom<A, C1, C2> cbf)
{
DB<C1> acc = DB.unit(cbf.createBuffer());
for (DB<A> db : xs)
{
DB<C1> fAcc = acc;
acc = db.bind(x -> fAcc.map(ys -> cbf.add(x, ys)));
}
return acc.map(cbf::build);
}
/**
* Shorthand of sequence() that returns an fj.data.List
*/
public static <A> DB<fj.data.List<A>> sequence(Iterable<DB<A>> xs)
{
return sequence(xs, fjListCanBuildFrom());
}
}