View Javadoc
1   package guru.mikelue.jdut;
2   
3   import java.sql.Connection;
4   import java.sql.SQLException;
5   import java.util.Optional;
6   import javax.sql.DataSource;
7   
8   import org.slf4j.Logger;
9   import org.slf4j.LoggerFactory;
10  
11  import guru.mikelue.jdut.datagrain.DataGrain;
12  import guru.mikelue.jdut.datagrain.DataRow;
13  import guru.mikelue.jdut.decorate.DataGrainDecorator;
14  import guru.mikelue.jdut.decorate.TableSchemaLoadingDecorator;
15  import guru.mikelue.jdut.jdbc.JdbcFunction;
16  import guru.mikelue.jdut.jdbc.function.DbRelease;
17  import guru.mikelue.jdut.operation.DataGrainOperator;
18  import guru.mikelue.jdut.operation.DataRowOperator;
19  import guru.mikelue.jdut.operation.DataRowsOperator;
20  
21  /**
22   * The main executor of {@link DataGrainOperator} for {@link DataGrain}.<br>
23   *
24   * <h3>Main functions</h3>
25   * <p>This object is responsible for retrieving {@link Connection} from {@link DataSource},
26   * and uses the connection to execute any method of {@link #conduct(DataGrain, DataGrainOperator)}.</p>
27   *
28   * <p>Before operating the action on data grain, this conductor would load database schema by {@link TableSchemaLoadingDecorator} object, which caches loaded schema of tables.</p>
29   *
30   * <h3>Afterward decorating</h3>
31   * <p>Every method provided by this object has an overloading method with additional {@link DataGrainDecorator},
32   * the decorator is used after the loading of table schema on the data grain.</p>
33   *
34   * <p>
35   * The building/cleaning actions defined by {@link DuetConductor}, however, doesn't know the decorator has decorated
36   * the data grain or not, you should be cautious about the re-decorating behaviour.<br>
37   *
38   * {@link DataRow} object has {@link DataRow#putAttribute putAttribute} and {@link DataRow#getAttribute getAttribute} method
39   * to let you keep supplementary information of the row. These method is useful for implementing {@link DataGrainDecorator}.
40   * </p>
41   */
42  public class DataConductor {
43  	private Logger logger = LoggerFactory.getLogger(DataConductor.class);
44  	private final DataSource dataSource;
45  	private final DataGrainDecorator schemaLoadingDecorator;
46  
47  	/**
48  	 * Constructs this object with a valid {@link DataSource} object.
49  	 *
50  	 * @param newDataSource The initialized object of data source
51  	 */
52  	public DataConductor(DataSource newDataSource)
53  	{
54  		dataSource = newDataSource;
55  		schemaLoadingDecorator = new TableSchemaLoadingDecorator(dataSource);
56  	}
57  
58  	/**
59  	 * Builds JDBC function, which loads schema and executes <em>operator</em> on
60  	 * <em>dataGrain</em> object.
61  	 *
62  	 * @param dataGrain The data grain object to be operated
63  	 * @param operator The operator to affects database for testing
64  	 * @param decorator The decorator to decorate the data grain after loading of table chema
65  	 *
66  	 * @return The function of JDBC
67  	 */
68  	public JdbcFunction<Connection, DataGrain> buildJdbcFunction(
69  		DataGrain dataGrain, DataGrainOperator operator,
70  		DataGrainDecorator decorator
71  	) {
72  		return conn -> {
73  			DataGrain dataGrainOfSchemaLoaded = dataGrain.decorate(schemaLoadingDecorator);
74  
75  			if (decorator != null) {
76  				dataGrainOfSchemaLoaded = dataGrain.decorate(decorator);
77  			}
78  
79  			return operator.toJdbcFunction(dataGrainOfSchemaLoaded)
80  				.asFunction().apply(conn);
81  		};
82  	}
83  	/**
84  	 * Builds JDBC function, which loads schema and executes <em>operator</em> on
85  	 * <em>dataGrain</em> object.
86  	 *
87  	 * @param dataGrain The data grain object to be operated
88  	 * @param operator The operator to affects database for testing
89  	 *
90  	 * @return The function of JDBC
91  	 */
92  	public JdbcFunction<Connection, DataGrain> buildJdbcFunction(
93  		DataGrain dataGrain, DataGrainOperator operator
94  	) {
95  		return buildJdbcFunction(dataGrain, operator, null);
96  	}
97  
98  	/**
99  	 * Gets connection of database and feeds it to <em>operator</em>.
100 	 *
101 	 * @param dataGrain The data grain to be processed
102 	 * @param operator The operator to be executed
103 	 *
104 	 * @return The processed data grain
105 	 */
106 	public DataGrain./../../guru/mikelue/jdut/datagrain/DataGrain.html#DataGrain">DataGrain conduct(DataGrain dataGrain, DataGrainOperator operator)
107 	{
108 		return conduct(dataGrain, operator, Optional.empty());
109 	}
110 
111 	/**
112 	 * Gets connection of database and feeds it to <em>operator</em>.
113 	 *
114 	 * @param dataGrain The data grain to be processed
115 	 * @param operator The operator to be executed
116 	 * @param decorator The decorator used after schema matching
117 	 *
118 	 * @return The processed data grain
119 	 */
120 	public DataGrain./../../guru/mikelue/jdut/datagrain/DataGrain.html#DataGrain">DataGrain conduct(DataGrain dataGrain, DataGrainOperator operator, DataGrainDecorator decorator)
121 	{
122 		return conduct(dataGrain, operator, Optional.of(decorator));
123 	}
124 	/**
125 	 * Gets connection of database and feeds it to <em>operator</em>.
126 	 *
127 	 * @param dataGrain The data grain to be processed
128 	 * @param operator The operator to be executed
129 	 *
130 	 * @return The processed data grain
131 	 */
132 	public DataGrain./../../guru/mikelue/jdut/datagrain/DataGrain.html#DataGrain">DataGrain conduct(DataGrain dataGrain, DataRowsOperator operator)
133 	{
134 		return conduct(dataGrain, operator.toDataGrainOperator());
135 	}
136 	/**
137 	 * Gets connection of database and feeds it to <em>operator</em>.
138 	 *
139 	 * @param dataGrain The data grain to be processed
140 	 * @param operator The operator to be executed
141 	 * @param decorator The decorator used after schema matching
142 	 *
143 	 * @return The processed data grain
144 	 */
145 	public DataGrain./../../guru/mikelue/jdut/datagrain/DataGrain.html#DataGrain">DataGrain conduct(DataGrain dataGrain, DataRowsOperator operator, DataGrainDecorator decorator)
146 	{
147 		return conduct(dataGrain, operator.toDataGrainOperator(), decorator);
148 	}
149 	/**
150 	 * Gets connection of database and feeds it to <em>operator</em>.
151 	 *
152 	 * @param dataGrain The data grain to be processed
153 	 * @param operator The operator to be executed
154 	 *
155 	 * @return The processed data grain
156 	 */
157 	public DataGrain./../../guru/mikelue/jdut/datagrain/DataGrain.html#DataGrain">DataGrain conduct(DataGrain dataGrain, DataRowOperator operator)
158 	{
159 		return conduct(dataGrain, operator.toDataGrainOperator());
160 	}
161 	/**
162 	 * Gets connection of database and feeds it to <em>operator</em>.
163 	 *
164 	 * @param dataGrain The data grain to be processed
165 	 * @param operator The operator to be executed
166 	 * @param decorator The decorator used after schema matching
167 	 *
168 	 * @return The processed data grain
169 	 */
170 	public DataGrain./../../guru/mikelue/jdut/datagrain/DataGrain.html#DataGrain">DataGrain conduct(DataGrain dataGrain, DataRowOperator operator, DataGrainDecorator decorator)
171 	{
172 		return conduct(dataGrain, operator.toDataGrainOperator(), decorator);
173 	}
174 
175 	private DataGrain./../../guru/mikelue/jdut/datagrain/DataGrain.html#DataGrain">DataGrain conduct(DataGrain dataGrain, DataGrainOperator operator, Optional<DataGrainDecorator> decorator)
176 	{
177 		dataGrain = dataGrain.decorate(schemaLoadingDecorator);
178 
179 		if (decorator.isPresent()) {
180 			dataGrain = dataGrain.decorate(decorator.get());
181 		}
182 
183 		return conduct(
184 			operator.toJdbcFunction(dataGrain)
185 		);
186 	}
187 
188 	/**
189 	 * Executes a {@link JdbcFunction}, the connection would be put into {@link ConductorContext}.
190 	 *
191 	 * @param <T> The type of returned value
192 	 * @param jdbcFunction The JDBC function to be executed
193 	 *
194 	 * @return The result of function
195 	 */
196 	public <T> T conduct(JdbcFunction<Connection, T> jdbcFunction)
197 	{
198 		try {
199 			return jdbcFunction
200 				.surroundedBy(
201 					f -> conn -> {
202 						logger.debug("Put connection to context: [{}]", conn);
203 						ConductorContext.setCurrentConnection(conn);
204 
205 						try {
206 							return f.applyJdbc(conn);
207 						} finally {
208 							logger.debug("Remove connection from context: [{}]", conn);
209 							ConductorContext.cleanCurrentConnection();
210 						}
211 					}
212 				)
213 				.surroundedBy(DbRelease::autoClose)
214 				.applyJdbc(dataSource.getConnection());
215 		} catch (SQLException e) {
216 			throw new DataConductException(e);
217 		}
218 	}
219 }