Project

General

Profile

« Previous | Next » 

Revision 0f9d4e5a

Added by Andreas Müller almost 11 years ago

Allow access to hibernate dialect via DatabaseTypeEnum, use this for foreign key handling in TableCreator and ColumnAdder/Changer

View differences:

cdmlib-persistence/src/main/java/eu/etaxonomy/cdm/database/update/TableCreator.java
17 17
import org.apache.commons.lang.StringUtils;
18 18
import org.apache.log4j.Logger;
19 19

  
20
import eu.etaxonomy.cdm.common.CdmUtils;
20 21
import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;
21 22
import eu.etaxonomy.cdm.database.DatabaseTypeEnum;
22 23
import eu.etaxonomy.cdm.database.ICdmDataSource;
24
import eu.etaxonomy.cdm.model.agent.AgentBase;
23 25

  
24 26
/**
25 27
 * @author a.mueller
......
89 91
	}
90 92

  
91 93

  
94
	@Override
95
	public List<ISchemaUpdaterStep> getInnerSteps() {
96
		return mnTablesStepList;
97
	}
98

  
99
	/**
100
	 * Fills the {@link #columnAdders} list.
101
	 */
92 102
	private void makeColumnAdders() {
93 103
		if (columnNames.size() != columnTypes.size()){
94 104
			throw new RuntimeException ("ColumnNames and columnTypes must be of same size. Step: " + getStepName());
......
117 127
			}
118 128
		}
119 129
	}
130
	
131
	/**
132
	 * fills the mnTablesStepList
133
	 */
134
	private void makeMnTables() {
135
		TableCreator tableCreator;
120 136

  
137
		if (this.includeAnnotatableEntity){
138
			//annotations
139
			stepName= "Add @tableName annotations";
140
			stepName = stepName.replace("@tableName", this.tableName);
141
			tableCreator = MnTableCreator.NewMnInstance(stepName, this.tableName, "Annotation", SchemaUpdaterBase.INCLUDE_AUDIT);
142
			mnTablesStepList.add(tableCreator);
121 143

  
122
	@Override
123
	public List<ISchemaUpdaterStep> getInnerSteps() {
124
		return mnTablesStepList;
125
	}
144
			//marker
145
			stepName= "Add @tableName marker";
146
			stepName = stepName.replace("@tableName", this.tableName);
147
			tableCreator = MnTableCreator.NewMnInstance(stepName, this.tableName, "Marker", SchemaUpdaterBase.INCLUDE_AUDIT);
148
			mnTablesStepList.add(tableCreator);
149
			
150
		}
151
		
152
		if (this.includeIdentifiableEntity){
126 153

  
127
	private String getColumnsSql(String tableName, ICdmDataSource datasource, IProgressMonitor monitor) throws DatabaseTypeNotSupportedException {
128
		String result = "";
129
		for (ColumnAdder adder : this.columnAdders){
130
			String singleAdderSQL = adder.getUpdateQueryString(tableName, datasource, monitor) + ", ";
154
			//credits
155
			stepName= "Add @tableName credits";
156
			stepName = stepName.replace("@tableName", this.tableName);
157
			tableCreator = MnTableCreator.NewMnInstance(stepName, this.tableName, null, "Credit", null, SchemaUpdaterBase.INCLUDE_AUDIT, SORT_INDEX, false);
158
			mnTablesStepList.add(tableCreator);
131 159
			
132
			String[] split = singleAdderSQL.split(ColumnAdder.getAddColumnSeperator(datasource));
133
			result += split[1];
160
			//extensions
161
			stepName= "Add @tableName extensions";
162
			stepName = stepName.replace("@tableName", this.tableName);
163
			tableCreator = MnTableCreator.NewMnInstance(stepName, this.tableName, "Extension", SchemaUpdaterBase.INCLUDE_AUDIT);
164
			mnTablesStepList.add(tableCreator);
165
			
166
			//OriginalSourceBase
167
			stepName= "Add @tableName sources";
168
			stepName = stepName.replace("@tableName", this.tableName);
169
			tableCreator = MnTableCreator.NewMnInstance(stepName, this.tableName, null, "OriginalSourceBase", "sources", SchemaUpdaterBase.INCLUDE_AUDIT, false, true);
170
			mnTablesStepList.add(tableCreator);
171

  
172
			//Rights
173
			stepName= "Add @tableName rights";
174
			stepName = stepName.replace("@tableName", this.tableName);
175
			tableCreator = MnTableCreator.NewMnInstance(stepName, this.tableName, "Rights", SchemaUpdaterBase.INCLUDE_AUDIT);
176
			mnTablesStepList.add(tableCreator);
134 177
		}
135
		return result;
136 178
	}
137 179

  
180

  
138 181
	@Override
139 182
	protected boolean invokeOnTable(String tableName, ICdmDataSource datasource, IProgressMonitor monitor)  {
140 183
		try {
141 184
			boolean result = true;
185
			//CREATE
142 186
			String updateQuery = "CREATE TABLE @tableName (";
187
			//AUDIT
143 188
			if (isAuditing){
144 189
				updateQuery += " REV integer not null, revtype tinyint, ";
145 190
			}
191
			//CdmBase
146 192
			if (includeCdmBaseAttributes){
147 193
					updateQuery += " id integer not null,"
148 194
						+ " created datetime, "
......
150 196
						+ " updated datetime, "
151 197
						+ " createdby_id integer,"
152 198
						+ " updatedby_id integer, ";
153
					
154 199
			}
200
			//EventBase
155 201
			if (this.includeEventBase){
156
				//TODO handle as column adder
157 202
				updateQuery += "timeperiod_start varchar(255), timeperiod_end varchar(255), timeperiod_freetext varchar(255), actor_id int, description varchar(255),";
158
				logger.warn("ForeignKey for actor not yet handled");
159 203
			}
160
			
204
			//Identifiable
161 205
			if (this.includeIdentifiableEntity){
162 206
				updateQuery += "lsid_authority varchar(255), lsid_lsid varchar(255), lsid_namespace varchar(255), lsid_object varchar(255), lsid_revision varchar(255), protectedtitlecache bit not null, titleCache varchar(255),";
163 207
			}
164
			
208
			//specific columns
165 209
			updateQuery += 	getColumnsSql(tableName, datasource, monitor);
166 210
			
211
			//primary and unique keys
167 212
			String primaryKeySql = primaryKey(isAuditing)==null ? "" : "primary key (" + primaryKey(isAuditing) + "),";
168 213
			String uniqueSql = unique(isAuditing)== null ? "" : "unique(" + unique(isAuditing) + "),";
169 214
			updateQuery += primaryKeySql + uniqueSql;
170 215
			
171
			updateQuery = StringUtils.chomp(updateQuery.trim(), ",");
172
			updateQuery += ")";
216
			//finalize
217
			updateQuery = StringUtils.chomp(updateQuery.trim(), ",") + ")";
173 218
			
219
			//replace
174 220
			updateQuery = updateQuery.replace("@tableName", tableName);
175
			if (datasource.getDatabaseType().equals(DatabaseTypeEnum.MySQL)){
176
				updateQuery += " ENGINE=MYISAM DEFAULT CHARSET=utf8 ";
177
			}
178
			logger.debug(updateQuery);
221
			
222
			//append datasource specific string
223
			updateQuery += datasource.getDatabaseType().getHibernateDialect().getTableTypeString();
224
			logger.debug("UPDATE Query: " + updateQuery);
225
			
226
			//execute
179 227
			datasource.executeUpdate(updateQuery);
228
			
229
			//Foreign Keys
180 230
			result &= createForeignKeys(tableName, isAuditing, datasource, monitor);
231
			
181 232
			return result;
182 233
		} catch (Exception e) {
183 234
			monitor.warning(e.getMessage(), e);
......
187 238
	}
188 239

  
189 240

  
190
	private void makeMnTables() {
191
		TableCreator tableCreator;
192

  
193
		if (this.includeAnnotatableEntity){
194
			//annotations
195
			stepName= "Add @tableName annotations";
196
			stepName = stepName.replace("@tableName", this.tableName);
197
			tableCreator = MnTableCreator.NewMnInstance(stepName, this.tableName, "Annotation", SchemaUpdaterBase.INCLUDE_AUDIT);
198
			mnTablesStepList.add(tableCreator);
199

  
200
			//marker
201
			stepName= "Add @tableName marker";
202
			stepName = stepName.replace("@tableName", this.tableName);
203
			tableCreator = MnTableCreator.NewMnInstance(stepName, this.tableName, "Marker", SchemaUpdaterBase.INCLUDE_AUDIT);
204
			mnTablesStepList.add(tableCreator);
205
			
206
		}
207
		
208
		if (this.includeIdentifiableEntity){
209

  
210
			//credits
211
			stepName= "Add @tableName credits";
212
			stepName = stepName.replace("@tableName", this.tableName);
213
			tableCreator = MnTableCreator.NewMnInstance(stepName, this.tableName, null, "Credit", null, SchemaUpdaterBase.INCLUDE_AUDIT, SORT_INDEX, false);
214
			mnTablesStepList.add(tableCreator);
215
			
216
			//extensions
217
			stepName= "Add @tableName extensions";
218
			stepName = stepName.replace("@tableName", this.tableName);
219
			tableCreator = MnTableCreator.NewMnInstance(stepName, this.tableName, "Extension", SchemaUpdaterBase.INCLUDE_AUDIT);
220
			mnTablesStepList.add(tableCreator);
221
			
222
			//OriginalSourceBase
223
			stepName= "Add @tableName sources";
224
			stepName = stepName.replace("@tableName", this.tableName);
225
			tableCreator = MnTableCreator.NewMnInstance(stepName, this.tableName, null, "OriginalSourceBase", "sources", SchemaUpdaterBase.INCLUDE_AUDIT, false, true);
226
			mnTablesStepList.add(tableCreator);
227

  
228
			//Rights
229
			stepName= "Add @tableName rights";
230
			stepName = stepName.replace("@tableName", this.tableName);
231
			tableCreator = MnTableCreator.NewMnInstance(stepName, this.tableName, "Rights", SchemaUpdaterBase.INCLUDE_AUDIT);
232
			mnTablesStepList.add(tableCreator);
241
	/**
242
	 * Returns the sql part for the {@link #columnAdders} columns.
243
	 * This is done by reusing the same method in the ColumnAdder class and removing all the prefixes like 'ADD COLUMN'
244
	 */
245
	private String getColumnsSql(String tableName, ICdmDataSource datasource, IProgressMonitor monitor) throws DatabaseTypeNotSupportedException {
246
		String result = "";
247
		for (ColumnAdder adder : this.columnAdders){
248
			String singleAdderSQL = adder.getUpdateQueryString(tableName, datasource, monitor) + ", ";
233 249
			
250
			String[] split = singleAdderSQL.split(ColumnAdder.getAddColumnSeperator(datasource));
251
			result += split[1];
234 252
		}
253
		return result;
235 254
	}
255

  
236 256
	
237 257
	private boolean createForeignKeys(String tableName, boolean isAudit, ICdmDataSource datasource, IProgressMonitor monitor) throws SQLException {
238 258
		boolean result = true;
239 259
		if (includeCdmBaseAttributes){
240 260
			String attribute = "updatedby";
241 261
			String referencedTable = "UserAccount";
242
			result &= makeForeignKey(tableName, datasource, attribute, referencedTable);
262
			result &= makeForeignKey(tableName, datasource, monitor, attribute, referencedTable);
243 263
			
244 264
			attribute = "createdby";
245 265
			referencedTable = "UserAccount";
246
			result &= makeForeignKey(tableName, datasource, attribute, referencedTable);			
266
			result &= makeForeignKey(tableName, datasource, monitor, attribute, referencedTable);			
247 267
		
248 268
		}
249 269
		if (isAudit){
250 270
			String attribute = "REV";
251 271
			String referencedTable = "AuditEvent";
252
			result &= makeForeignKey(tableName, datasource, attribute, referencedTable);
272
			result &= makeForeignKey(tableName, datasource, monitor, attribute, referencedTable);
273
		}
274
		if (this.includeEventBase){
275
			String attribute = "actor_id";
276
			String referencedTable = "AgentBase";
277
			result &= makeForeignKey(tableName, datasource, monitor, attribute, referencedTable);
253 278
		}
254 279
		for (ColumnAdder adder : this.columnAdders){
255 280
			if (adder.getReferencedTable() != null){
256
				result &= makeForeignKey(tableName, datasource, adder.getNewColumnName(), adder.getReferencedTable()); 
281
				result &= makeForeignKey(tableName, datasource, monitor, adder.getNewColumnName(), adder.getReferencedTable()); 
257 282
			}
258 283
		}
259 284
		return result;
260 285
	}
261 286

  
262
	public static boolean makeForeignKey(String tableName, ICdmDataSource datasource, String attribute, String referencedTable) throws SQLException {
287
	public static boolean makeForeignKey(String tableName, ICdmDataSource datasource, IProgressMonitor monitor, String attribute, String referencedTable) throws SQLException {
263 288
		boolean result = true;
264
		String index = "FK@tableName_@attribute";
265
		index = index.replace("@tableName", tableName);
266
		index = index.replace("@attribute", attribute);
267 289
		
268
		String idSuffix = "_id";
269
		if ("REV".equalsIgnoreCase(attribute) || attribute.endsWith(idSuffix)){
270
			idSuffix = "";
290
		if (supportsForeignKeys(datasource, monitor, tableName, referencedTable)){
291
			String index = "FK@tableName_@attribute";
292
			index = index.replace("@tableName", tableName);
293
			index = index.replace("@attribute", attribute);
294
			
295
			String idSuffix = "_id";
296
			if (isRevAttribute(attribute) || attribute.endsWith(idSuffix)){
297
				idSuffix = "";
298
			}
299
			String updateQuery = "ALTER TABLE @tableName ADD INDEX @index (@attribute), ADD CONSTRAINT @index FOREIGN KEY (@attribute) REFERENCES @referencedTable (@id)";
300
			updateQuery = updateQuery.replace("@tableName", tableName);
301
			updateQuery = updateQuery.replace("@index", index);
302
			updateQuery = updateQuery.replace("@attribute", attribute + idSuffix);
303
			updateQuery = updateQuery.replace("@referencedTable", referencedTable);
304
			if (isRevAttribute(attribute)){
305
				updateQuery = updateQuery.replace("@id", "revisionnumber");
306
			}else{
307
				updateQuery = updateQuery.replace("@id", "id");
308
			}
309
			logger.debug(updateQuery);
310
			try {
311
				datasource.executeUpdate(updateQuery);
312
			} catch (Exception e) {
313
				String message = "Problem when creating Foreign Key for " + tableName +"." + attribute +": " + e.getMessage();
314
				monitor.warning(message);
315
				logger.warn(message, e);
316
				return true;
317
			}
318
			return result;			
319
		}else{
320
			return true;
271 321
		}
272
		String updateQuery = "ALTER TABLE @tableName ADD INDEX @index (@attribute), ADD CONSTRAINT @index FOREIGN KEY (@attribute) REFERENCES @referencedTable (id)";
273
		updateQuery = updateQuery.replace("@tableName", tableName);
274
		updateQuery = updateQuery.replace("@index", index);
275
		updateQuery = updateQuery.replace("@attribute", attribute + idSuffix);
276
		updateQuery = updateQuery.replace("@referencedTable", referencedTable);
277
		
278
		logger.debug(updateQuery);
279
		try {
280
			datasource.executeUpdate(updateQuery);
281
		} catch (SQLException e) {
282
			throw e;
322

  
323
	}
324

  
325
	/**
326
	 * Determines if the tables and the database support foreign keys. If determination is not possible true is returned as default.
327
	 * @param datasource
328
	 * @param monitor
329
	 * @param tableName
330
	 * @param referencedTable
331
	 * @return
332
	 */
333
	private static boolean supportsForeignKeys(ICdmDataSource datasource, IProgressMonitor monitor, String tableName, String referencedTable) {
334
		boolean result = true;
335
		if (! datasource.getDatabaseType().equals(DatabaseTypeEnum.MySQL)){
336
			return true;
337
		}else{
338
			try {
339
				String myIsamTables = "";
340
				String format = "SELECT ENGINE FROM information_schema.TABLES where TABLE_SCHEMA = '%s' AND TABLE_NAME = '%s'";
341
				String sql = String.format(format, datasource.getDatabase(), tableName);
342
				String engine = (String)datasource.getSingleValue(sql);
343
				if (engine.equals("MyISAM")){
344
					result = false;
345
					myIsamTables = CdmUtils.concat(",", myIsamTables, tableName);
346
				}
347
				sql = String.format(format,  datasource.getDatabase(), referencedTable);
348
				engine = (String)datasource.getSingleValue(sql);
349
				if (engine.equals("MyISAM")){
350
					result = false;
351
					myIsamTables = CdmUtils.concat(",", myIsamTables, referencedTable);
352
				}
353
				if (result == false){
354
					String message = "Tables (%s) use MyISAM engine. MyISAM does not support foreign keys.";
355
					message = String.format(message, myIsamTables);
356
					monitor.warning(message);
357
				}
358
				return result;
359
			} catch (Exception e) {
360
				String message = "Problems to determine table engine for MySQL.";
361
				monitor.warning(message);
362
				return true;  //default 
363
			}
364
			
283 365
		}
284
		return result;
366
		
367
				
368
	}
369

  
370
	private static boolean isRevAttribute(String attribute) {
371
		return "REV".equalsIgnoreCase(attribute);
285 372
	}
286 373

  
287 374

  
375
	/**
376
	 * Constructs the primary key creation string
377
	 * @param isAudit
378
	 * @return
379
	 */
288 380
	protected String primaryKey(boolean isAudit){
289 381
		String result = null;
290 382
		if (! isAudit && this.primaryKeyParams != null){ 
......
303 395
		return result;
304 396
	}
305 397
	
398
	/**
399
	 * Constructs the unique key creation string
400
	 * @param isAudit
401
	 * @return
402
	 */
306 403
	protected String unique(boolean isAudit){
307 404
		if (! isAudit){
308 405
			if (this.uniqueParams != null){

Also available in: Unified diff