001package io.ebean.datasource; 002 003import java.sql.Connection; 004import java.util.ArrayList; 005import java.util.LinkedHashMap; 006import java.util.List; 007import java.util.Map; 008import java.util.Properties; 009 010/** 011 * Configuration information for a DataSource. 012 */ 013public class DataSourceConfig { 014 015 private static final String POSTGRES = "postgres"; 016 017 private InitDatabase initDatabase; 018 019 private String url; 020 021 private String username; 022 023 private String password; 024 025 private String schema; 026 027 /** 028 * The name of the database platform (for use with ownerUsername and InitDatabase). 029 */ 030 private String platform; 031 032 /** 033 * The optional database owner username (for running InitDatabase). 034 */ 035 private String ownerUsername; 036 037 /** 038 * The optional database owner password (for running InitDatabase). 039 */ 040 private String ownerPassword; 041 042 private String driver; 043 044 private int minConnections = 2; 045 046 private int maxConnections = 200; 047 048 private int isolationLevel = Connection.TRANSACTION_READ_COMMITTED; 049 050 private boolean autoCommit; 051 052 private boolean readOnly; 053 054 private String heartbeatSql; 055 056 private int heartbeatFreqSecs = 30; 057 058 private int heartbeatTimeoutSeconds = 3; 059 060 private boolean captureStackTrace; 061 062 private int maxStackTraceSize = 5; 063 064 private int leakTimeMinutes = 30; 065 066 private int maxInactiveTimeSecs = 300; 067 068 private int maxAgeMinutes = 0; 069 070 private int trimPoolFreqSecs = 59; 071 072 private int pstmtCacheSize = 20; 073 074 private int cstmtCacheSize = 20; 075 076 private int waitTimeoutMillis = 1000; 077 078 private String poolListener; 079 080 private boolean offline; 081 082 private boolean failOnStart = true; 083 084 private Map<String, String> customProperties; 085 086 private List<String> initSql; 087 088 089 private DataSourceAlert alert; 090 091 private DataSourcePoolListener listener; 092 093 /** 094 * Return a copy of the DataSourceConfig. 095 */ 096 public DataSourceConfig copy() { 097 098 DataSourceConfig copy = new DataSourceConfig(); 099 copy.initDatabase = initDatabase; 100 copy.url = url; 101 copy.username = username; 102 copy.password = password; 103 copy.schema = schema; 104 copy.platform = platform; 105 copy.ownerUsername = ownerUsername; 106 copy.ownerPassword = ownerPassword; 107 copy.driver = driver; 108 copy.minConnections = minConnections; 109 copy.maxConnections = maxConnections; 110 copy.isolationLevel = isolationLevel; 111 copy.autoCommit = autoCommit; 112 copy.readOnly = readOnly; 113 copy.heartbeatSql = heartbeatSql; 114 copy.heartbeatFreqSecs = heartbeatFreqSecs; 115 copy.heartbeatTimeoutSeconds = heartbeatTimeoutSeconds; 116 copy.captureStackTrace = captureStackTrace; 117 copy.maxStackTraceSize = maxStackTraceSize; 118 copy.leakTimeMinutes = leakTimeMinutes; 119 copy.maxInactiveTimeSecs = maxInactiveTimeSecs; 120 copy.maxAgeMinutes = maxAgeMinutes; 121 copy.trimPoolFreqSecs = trimPoolFreqSecs; 122 copy.pstmtCacheSize = pstmtCacheSize; 123 copy.cstmtCacheSize = cstmtCacheSize; 124 copy.waitTimeoutMillis = waitTimeoutMillis; 125 copy.poolListener = poolListener; 126 copy.offline = offline; 127 copy.failOnStart = failOnStart; 128 if (customProperties != null) { 129 copy.customProperties = new LinkedHashMap<>(customProperties); 130 } 131 copy.initSql = initSql; 132 copy.alert = alert; 133 copy.listener = listener; 134 135 return copy; 136 } 137 138 /** 139 * Default the values for driver, url, username and password from another config if 140 * they have not been set. 141 */ 142 public DataSourceConfig setDefaults(DataSourceConfig other) { 143 if (driver == null) { 144 driver = other.driver; 145 } 146 if (url == null) { 147 url = other.url; 148 } 149 if (username == null) { 150 username = other.username; 151 } 152 if (password == null) { 153 password = other.password; 154 } 155 if (schema == null) { 156 schema = other.schema; 157 } 158 return this; 159 } 160 161 /** 162 * Return true if there are no values set for any of url, driver, username and password. 163 */ 164 public boolean isEmpty() { 165 return url == null 166 && driver == null 167 && username == null 168 && password == null; 169 } 170 171 /** 172 * Return the connection URL. 173 */ 174 public String getUrl() { 175 return url; 176 } 177 178 /** 179 * Set the connection URL. 180 */ 181 public DataSourceConfig setUrl(String url) { 182 this.url = url; 183 return this; 184 } 185 186 /** 187 * Return the database username. 188 */ 189 public String getUsername() { 190 return username; 191 } 192 193 /** 194 * Set the database username. 195 */ 196 public DataSourceConfig setUsername(String username) { 197 this.username = username; 198 return this; 199 } 200 201 /** 202 * Return the database password. 203 */ 204 public String getPassword() { 205 return password; 206 } 207 208 /** 209 * Set the database password. 210 */ 211 public DataSourceConfig setPassword(String password) { 212 this.password = password; 213 return this; 214 } 215 216 /** 217 * Return the database username. 218 */ 219 public String getSchema() { 220 return schema; 221 } 222 223 /** 224 * Set the default database schema to use. 225 */ 226 public DataSourceConfig setSchema(String schema) { 227 this.schema = schema; 228 return this; 229 } 230 231 /** 232 * Return the database driver. 233 */ 234 public String getDriver() { 235 return driver; 236 } 237 238 /** 239 * Set the database driver. 240 */ 241 public DataSourceConfig setDriver(String driver) { 242 this.driver = driver; 243 return this; 244 } 245 246 /** 247 * Return the transaction isolation level. 248 */ 249 public int getIsolationLevel() { 250 return isolationLevel; 251 } 252 253 /** 254 * Set the transaction isolation level. 255 */ 256 public DataSourceConfig setIsolationLevel(int isolationLevel) { 257 this.isolationLevel = isolationLevel; 258 return this; 259 } 260 261 /** 262 * Return autoCommit setting. 263 */ 264 public boolean isAutoCommit() { 265 return autoCommit; 266 } 267 268 /** 269 * Set to true to turn on autoCommit. 270 */ 271 public DataSourceConfig setAutoCommit(boolean autoCommit) { 272 this.autoCommit = autoCommit; 273 return this; 274 } 275 276 /** 277 * Return the read only setting. 278 */ 279 public boolean isReadOnly() { 280 return readOnly; 281 } 282 283 /** 284 * Set to true to for read only. 285 */ 286 public DataSourceConfig setReadOnly(boolean readOnly) { 287 this.readOnly = readOnly; 288 return this; 289 } 290 291 /** 292 * Return the minimum number of connections the pool should maintain. 293 */ 294 public int getMinConnections() { 295 return minConnections; 296 } 297 298 /** 299 * Set the minimum number of connections the pool should maintain. 300 */ 301 public DataSourceConfig setMinConnections(int minConnections) { 302 this.minConnections = minConnections; 303 return this; 304 } 305 306 /** 307 * Return the maximum number of connections the pool can reach. 308 */ 309 public int getMaxConnections() { 310 return maxConnections; 311 } 312 313 /** 314 * Set the maximum number of connections the pool can reach. 315 */ 316 public DataSourceConfig setMaxConnections(int maxConnections) { 317 this.maxConnections = maxConnections; 318 return this; 319 } 320 321 /** 322 * Return the alert implementation to use. 323 */ 324 public DataSourceAlert getAlert() { 325 return alert; 326 } 327 328 /** 329 * Set the alert implementation to use. 330 */ 331 public DataSourceConfig setAlert(DataSourceAlert alert) { 332 this.alert = alert; 333 return this; 334 } 335 336 /** 337 * Return the listener to use. 338 */ 339 public DataSourcePoolListener getListener() { 340 return listener; 341 } 342 343 /** 344 * Set the listener to use. 345 */ 346 public DataSourceConfig setListener(DataSourcePoolListener listener) { 347 this.listener = listener; 348 return this; 349 } 350 351 /** 352 * Return a SQL statement used to test the database is accessible. 353 * <p> 354 * Note that if this is not set then it can get defaulted from the 355 * DatabasePlatform. 356 * </p> 357 */ 358 public String getHeartbeatSql() { 359 return heartbeatSql; 360 } 361 362 /** 363 * Set a SQL statement used to test the database is accessible. 364 * <p> 365 * Note that if this is not set then it can get defaulted from the 366 * DatabasePlatform. 367 * </p> 368 */ 369 public DataSourceConfig setHeartbeatSql(String heartbeatSql) { 370 this.heartbeatSql = heartbeatSql; 371 return this; 372 } 373 374 /** 375 * Return the heartbeat frequency in seconds. 376 * <p> 377 * This is the expected frequency in which the DataSource should be checked to 378 * make sure it is healthy and trim idle connections. 379 * </p> 380 */ 381 public int getHeartbeatFreqSecs() { 382 return heartbeatFreqSecs; 383 } 384 385 /** 386 * Set the expected heartbeat frequency in seconds. 387 */ 388 public DataSourceConfig setHeartbeatFreqSecs(int heartbeatFreqSecs) { 389 this.heartbeatFreqSecs = heartbeatFreqSecs; 390 return this; 391 } 392 393 /** 394 * Return the heart beat timeout in seconds. 395 */ 396 public int getHeartbeatTimeoutSeconds() { 397 return heartbeatTimeoutSeconds; 398 } 399 400 /** 401 * Set the heart beat timeout in seconds. 402 */ 403 public DataSourceConfig setHeartbeatTimeoutSeconds(int heartbeatTimeoutSeconds) { 404 this.heartbeatTimeoutSeconds = heartbeatTimeoutSeconds; 405 return this; 406 } 407 408 /** 409 * Return true if a stack trace should be captured when obtaining a connection 410 * from the pool. 411 * <p> 412 * This can be used to diagnose a suspected connection pool leak. 413 * </p> 414 * <p> 415 * Obviously this has a performance overhead. 416 * </p> 417 */ 418 public boolean isCaptureStackTrace() { 419 return captureStackTrace; 420 } 421 422 /** 423 * Set to true if a stack trace should be captured when obtaining a connection 424 * from the pool. 425 * <p> 426 * This can be used to diagnose a suspected connection pool leak. 427 * </p> 428 * <p> 429 * Obviously this has a performance overhead. 430 * </p> 431 */ 432 public DataSourceConfig setCaptureStackTrace(boolean captureStackTrace) { 433 this.captureStackTrace = captureStackTrace; 434 return this; 435 } 436 437 /** 438 * Return the max size for reporting stack traces on busy connections. 439 */ 440 public int getMaxStackTraceSize() { 441 return maxStackTraceSize; 442 } 443 444 /** 445 * Set the max size for reporting stack traces on busy connections. 446 */ 447 public DataSourceConfig setMaxStackTraceSize(int maxStackTraceSize) { 448 this.maxStackTraceSize = maxStackTraceSize; 449 return this; 450 } 451 452 /** 453 * Return the time in minutes after which a connection could be considered to 454 * have leaked. 455 */ 456 public int getLeakTimeMinutes() { 457 return leakTimeMinutes; 458 } 459 460 /** 461 * Set the time in minutes after which a connection could be considered to 462 * have leaked. 463 */ 464 public DataSourceConfig setLeakTimeMinutes(int leakTimeMinutes) { 465 this.leakTimeMinutes = leakTimeMinutes; 466 return this; 467 } 468 469 /** 470 * Return the size of the PreparedStatement cache (per connection). 471 */ 472 public int getPstmtCacheSize() { 473 return pstmtCacheSize; 474 } 475 476 /** 477 * Set the size of the PreparedStatement cache (per connection). 478 */ 479 public DataSourceConfig setPstmtCacheSize(int pstmtCacheSize) { 480 this.pstmtCacheSize = pstmtCacheSize; 481 return this; 482 } 483 484 /** 485 * Return the size of the CallableStatement cache (per connection). 486 */ 487 public int getCstmtCacheSize() { 488 return cstmtCacheSize; 489 } 490 491 /** 492 * Set the size of the CallableStatement cache (per connection). 493 */ 494 public DataSourceConfig setCstmtCacheSize(int cstmtCacheSize) { 495 this.cstmtCacheSize = cstmtCacheSize; 496 return this; 497 } 498 499 /** 500 * Return the time in millis to wait for a connection before timing out once 501 * the pool has reached its maximum size. 502 */ 503 public int getWaitTimeoutMillis() { 504 return waitTimeoutMillis; 505 } 506 507 /** 508 * Set the time in millis to wait for a connection before timing out once the 509 * pool has reached its maximum size. 510 */ 511 public DataSourceConfig setWaitTimeoutMillis(int waitTimeoutMillis) { 512 this.waitTimeoutMillis = waitTimeoutMillis; 513 return this; 514 } 515 516 /** 517 * Return the time in seconds a connection can be idle after which it can be 518 * trimmed from the pool. 519 * <p> 520 * This is so that the pool after a busy period can trend over time back 521 * towards the minimum connections. 522 * </p> 523 */ 524 public int getMaxInactiveTimeSecs() { 525 return maxInactiveTimeSecs; 526 } 527 528 /** 529 * Return the maximum age a connection is allowed to be before it is closed. 530 * <p> 531 * This can be used to close really old connections. 532 * </p> 533 */ 534 public int getMaxAgeMinutes() { 535 return maxAgeMinutes; 536 } 537 538 /** 539 * Set the maximum age a connection can be in minutes. 540 */ 541 public DataSourceConfig setMaxAgeMinutes(int maxAgeMinutes) { 542 this.maxAgeMinutes = maxAgeMinutes; 543 return this; 544 } 545 546 /** 547 * Set the time in seconds a connection can be idle after which it can be 548 * trimmed from the pool. 549 * <p> 550 * This is so that the pool after a busy period can trend over time back 551 * towards the minimum connections. 552 * </p> 553 */ 554 public DataSourceConfig setMaxInactiveTimeSecs(int maxInactiveTimeSecs) { 555 this.maxInactiveTimeSecs = maxInactiveTimeSecs; 556 return this; 557 } 558 559 560 /** 561 * Return the minimum time gap between pool trim checks. 562 * <p> 563 * This defaults to 59 seconds meaning that the pool trim check will run every 564 * minute assuming the heart beat check runs every 30 seconds. 565 * </p> 566 */ 567 public int getTrimPoolFreqSecs() { 568 return trimPoolFreqSecs; 569 } 570 571 /** 572 * Set the minimum trim gap between pool trim checks. 573 */ 574 public DataSourceConfig setTrimPoolFreqSecs(int trimPoolFreqSecs) { 575 this.trimPoolFreqSecs = trimPoolFreqSecs; 576 return this; 577 } 578 579 /** 580 * Return the pool listener. 581 */ 582 public String getPoolListener() { 583 return poolListener; 584 } 585 586 /** 587 * Set a pool listener. 588 */ 589 public DataSourceConfig setPoolListener(String poolListener) { 590 this.poolListener = poolListener; 591 return this; 592 } 593 594 /** 595 * Return true if the DataSource should be left offline. 596 * <p> 597 * This is to support DDL generation etc without having a real database. 598 * </p> 599 */ 600 public boolean isOffline() { 601 return offline; 602 } 603 604 /** 605 * Return true (default) if the DataSource should be fail on start. 606 * <p> 607 * This enables to initialize the Ebean-Server if the db-server is not yet up. 608 * ({@link DataSourceAlert#dataSourceUp(javax.sql.DataSource)} is fired when DS gets up later.) 609 * </p> 610 */ 611 public boolean isFailOnStart() { 612 return failOnStart; 613 } 614 615 /** 616 * Set to false, if DataSource should not fail on start. (e.g. DataSource is not available) 617 */ 618 public DataSourceConfig setFailOnStart(boolean failOnStart) { 619 this.failOnStart = failOnStart; 620 return this; 621 } 622 623 /** 624 * Set to true if the DataSource should be left offline. 625 */ 626 public DataSourceConfig setOffline(boolean offline) { 627 this.offline = offline; 628 return this; 629 } 630 631 /** 632 * Return a map of custom properties for the jdbc driver connection. 633 */ 634 public Map<String, String> getCustomProperties() { 635 return customProperties; 636 } 637 638 /** 639 * Return a list of init queries, that are executed after a connection is opened. 640 */ 641 public List<String> getInitSql() { 642 return initSql; 643 } 644 645 /** 646 * Set custom init queries for each query. 647 */ 648 public DataSourceConfig setInitSql(List<String> initSql) { 649 this.initSql = initSql; 650 return this; 651 } 652 653 /** 654 * Set custom properties for the jdbc driver connection. 655 */ 656 public DataSourceConfig setCustomProperties(Map<String, String> customProperties) { 657 this.customProperties = customProperties; 658 return this; 659 } 660 661 /** 662 * Return the database owner username. 663 */ 664 public String getOwnerUsername() { 665 return ownerUsername; 666 } 667 668 /** 669 * Set the database owner username (used to create connection for use with InitDatabase). 670 */ 671 public DataSourceConfig setOwnerUsername(String ownerUsername) { 672 this.ownerUsername = ownerUsername; 673 return this; 674 } 675 676 /** 677 * Return the database owner password. 678 */ 679 public String getOwnerPassword() { 680 return ownerPassword; 681 } 682 683 /** 684 * Set the database owner password (used to create connection for use with InitDatabase). 685 */ 686 public DataSourceConfig setOwnerPassword(String ownerPassword) { 687 this.ownerPassword = ownerPassword; 688 return this; 689 } 690 691 /** 692 * Return the database platform. 693 */ 694 public String getPlatform() { 695 return platform; 696 } 697 698 /** 699 * Set the database platform (for use with ownerUsername and InitDatabase. 700 */ 701 public DataSourceConfig setPlatform(String platform) { 702 this.platform = platform; 703 if (initDatabase != null) { 704 setInitDatabaseForPlatform(platform); 705 } 706 return this; 707 } 708 709 /** 710 * Return the InitDatabase to use with ownerUsername. 711 */ 712 public InitDatabase getInitDatabase() { 713 return initDatabase; 714 } 715 716 /** 717 * Set the InitDatabase to use with ownerUsername. 718 */ 719 public DataSourceConfig setInitDatabase(InitDatabase initDatabase) { 720 this.initDatabase = initDatabase; 721 return this; 722 } 723 724 /** 725 * Set InitDatabase based on the database platform. 726 */ 727 public DataSourceConfig setInitDatabaseForPlatform(String platform) { 728 if (platform != null) { 729 switch (platform.toLowerCase()) { 730 case POSTGRES: 731 initDatabase = new PostgresInitDatabase(); 732 break; 733 } 734 } 735 return this; 736 } 737 738 /** 739 * Return true if InitDatabase should be used (when the pool initialises and a connection can't be obtained). 740 * 741 * @return True to obtain a connection using ownerUsername and run InitDatabase. 742 */ 743 public boolean useInitDatabase() { 744 if (ownerUsername != null && ownerPassword != null) { 745 if (initDatabase == null) { 746 // default to postgres 747 initDatabase = new PostgresInitDatabase(); 748 } 749 return true; 750 } 751 return false; 752 } 753 754 /** 755 * Load the settings from the properties supplied. 756 * <p> 757 * You can use this when you have your own properties to use for configuration. 758 * </p> 759 * 760 * @param properties the properties to configure the dataSource 761 * @param serverName the name of the specific dataSource (optional) 762 */ 763 public DataSourceConfig loadSettings(Properties properties, String serverName) { 764 ConfigPropertiesHelper dbProps = new ConfigPropertiesHelper("datasource", serverName, properties); 765 loadSettings(dbProps); 766 return this; 767 } 768 769 /** 770 * Load the settings from the PropertiesWrapper. 771 */ 772 private void loadSettings(ConfigPropertiesHelper properties) { 773 774 username = properties.get("username", username); 775 password = properties.get("password", password); 776 schema = properties.get("schema", schema); 777 platform = properties.get("platform", platform); 778 ownerUsername = properties.get("ownerUsername", ownerUsername); 779 ownerPassword = properties.get("ownerPassword", ownerPassword); 780 if (initDatabase == null && platform != null) { 781 setInitDatabaseForPlatform(platform); 782 } 783 784 driver = properties.get("driver", properties.get("databaseDriver", driver)); 785 url = properties.get("url", properties.get("databaseUrl", url)); 786 autoCommit = properties.getBoolean("autoCommit", autoCommit); 787 readOnly = properties.getBoolean("readOnly", readOnly); 788 captureStackTrace = properties.getBoolean("captureStackTrace", captureStackTrace); 789 maxStackTraceSize = properties.getInt("maxStackTraceSize", maxStackTraceSize); 790 leakTimeMinutes = properties.getInt("leakTimeMinutes", leakTimeMinutes); 791 maxInactiveTimeSecs = properties.getInt("maxInactiveTimeSecs", maxInactiveTimeSecs); 792 trimPoolFreqSecs = properties.getInt("trimPoolFreqSecs", trimPoolFreqSecs); 793 maxAgeMinutes = properties.getInt("maxAgeMinutes", maxAgeMinutes); 794 795 minConnections = properties.getInt("minConnections", minConnections); 796 maxConnections = properties.getInt("maxConnections", maxConnections); 797 pstmtCacheSize = properties.getInt("pstmtCacheSize", pstmtCacheSize); 798 cstmtCacheSize = properties.getInt("cstmtCacheSize", cstmtCacheSize); 799 800 waitTimeoutMillis = properties.getInt("waitTimeout", waitTimeoutMillis); 801 802 heartbeatSql = properties.get("heartbeatSql", heartbeatSql); 803 heartbeatTimeoutSeconds = properties.getInt("heartbeatTimeoutSeconds", heartbeatTimeoutSeconds); 804 poolListener = properties.get("poolListener", poolListener); 805 offline = properties.getBoolean("offline", offline); 806 807 String isoLevel = properties.get("isolationLevel", getTransactionIsolationLevel(isolationLevel)); 808 this.isolationLevel = getTransactionIsolationLevel(isoLevel); 809 810 this.initSql = parseSql(properties.get("initSql", null)); 811 this.failOnStart = properties.getBoolean("failOnStart", failOnStart); 812 813 String customProperties = properties.get("customProperties", null); 814 if (customProperties != null && customProperties.length() > 0) { 815 this.customProperties = parseCustom(customProperties); 816 } 817 } 818 819 private List<String> parseSql(String sql) { 820 List<String> ret = new ArrayList<>(); 821 if (sql != null) { 822 String[] queries = sql.split(";"); 823 for (String query : queries) { 824 query = query.trim(); 825 if (!query.isEmpty()) { 826 ret.add(query); 827 } 828 } 829 } 830 return ret; 831 } 832 833 Map<String, String> parseCustom(String customProperties) { 834 835 Map<String, String> propertyMap = new LinkedHashMap<String, String>(); 836 String[] pairs = customProperties.split(";"); 837 for (String pair : pairs) { 838 String[] split = pair.split("="); 839 if (split.length == 2) { 840 propertyMap.put(split[0], split[1]); 841 } 842 } 843 return propertyMap; 844 } 845 846 /** 847 * Return the isolation level description from the associated Connection int value. 848 */ 849 private String getTransactionIsolationLevel(int level) { 850 switch (level) { 851 case Connection.TRANSACTION_NONE: 852 return "NONE"; 853 case Connection.TRANSACTION_READ_COMMITTED: 854 return "READ_COMMITTED"; 855 case Connection.TRANSACTION_READ_UNCOMMITTED: 856 return "READ_UNCOMMITTED"; 857 case Connection.TRANSACTION_REPEATABLE_READ: 858 return "REPEATABLE_READ"; 859 case Connection.TRANSACTION_SERIALIZABLE: 860 return "SERIALIZABLE"; 861 default: 862 throw new RuntimeException("Transaction Isolation level [" + level + "] is not known."); 863 } 864 } 865 866 /** 867 * Return the isolation level for a given string description. 868 */ 869 private int getTransactionIsolationLevel(String level) { 870 level = level.toUpperCase(); 871 if (level.startsWith("TRANSACTION")) { 872 level = level.substring("TRANSACTION".length()); 873 } 874 level = level.replace("_", ""); 875 if ("NONE".equalsIgnoreCase(level)) { 876 return Connection.TRANSACTION_NONE; 877 } 878 if ("READCOMMITTED".equalsIgnoreCase(level)) { 879 return Connection.TRANSACTION_READ_COMMITTED; 880 } 881 if ("READUNCOMMITTED".equalsIgnoreCase(level)) { 882 return Connection.TRANSACTION_READ_UNCOMMITTED; 883 } 884 if ("REPEATABLEREAD".equalsIgnoreCase(level)) { 885 return Connection.TRANSACTION_REPEATABLE_READ; 886 } 887 if ("SERIALIZABLE".equalsIgnoreCase(level)) { 888 return Connection.TRANSACTION_SERIALIZABLE; 889 } 890 891 throw new RuntimeException("Transaction Isolation level [" + level + "] is not known."); 892 } 893}