Move configuration store to db
This commit is contained in:
		
							parent
							
								
									90ec01bfbf
								
							
						
					
					
						commit
						c0f771684d
					
				| @ -1111,6 +1111,13 @@ | ||||
|   "queryAllDeclaredConstructors":true, | ||||
|   "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Boolean","java.lang.Boolean","java.lang.Boolean","java.lang.Boolean","org.asamk.signal.manager.api.PhoneNumberSharingMode"] }, {"name":"linkPreviews","parameterTypes":[] }, {"name":"phoneNumberSharingMode","parameterTypes":[] }, {"name":"phoneNumberUnlisted","parameterTypes":[] }, {"name":"readReceipts","parameterTypes":[] }, {"name":"typingIndicators","parameterTypes":[] }, {"name":"unidentifiedDeliveryIndicators","parameterTypes":[] }] | ||||
| }, | ||||
| { | ||||
|   "name":"org.asamk.signal.manager.storage.configuration.LegacyConfigurationStore$Storage", | ||||
|   "allDeclaredFields":true, | ||||
|   "queryAllDeclaredMethods":true, | ||||
|   "queryAllDeclaredConstructors":true, | ||||
|   "methods":[{"name":"<init>","parameterTypes":["java.lang.Boolean","java.lang.Boolean","java.lang.Boolean","java.lang.Boolean","java.lang.Boolean","org.asamk.signal.manager.api.PhoneNumberSharingMode"] }] | ||||
| }, | ||||
| { | ||||
|   "name":"org.asamk.signal.manager.storage.contacts.LegacyContactInfo", | ||||
|   "allDeclaredFields":true, | ||||
|  | ||||
| @ -30,7 +30,7 @@ import java.util.UUID; | ||||
| public class AccountDatabase extends Database { | ||||
| 
 | ||||
|     private final static Logger logger = LoggerFactory.getLogger(AccountDatabase.class); | ||||
|     private static final long DATABASE_VERSION = 16; | ||||
|     private static final long DATABASE_VERSION = 17; | ||||
| 
 | ||||
|     private AccountDatabase(final HikariDataSource dataSource) { | ||||
|         super(logger, DATABASE_VERSION, dataSource); | ||||
| @ -503,5 +503,17 @@ public class AccountDatabase extends Database { | ||||
|                                         """); | ||||
|             } | ||||
|         } | ||||
|         if (oldVersion < 17) { | ||||
|             logger.debug("Updating database: Adding key_value table"); | ||||
|             try (final var statement = connection.createStatement()) { | ||||
|                 statement.executeUpdate(""" | ||||
|                                         CREATE TABLE key_value ( | ||||
|                                                 _id INTEGER PRIMARY KEY, | ||||
|                                                 key TEXT UNIQUE NOT NULL, | ||||
|                                                 value ANY | ||||
|                                         ) STRICT; | ||||
|                                         """); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -12,6 +12,7 @@ import org.asamk.signal.manager.api.ServiceEnvironment; | ||||
| import org.asamk.signal.manager.api.TrustLevel; | ||||
| import org.asamk.signal.manager.helper.RecipientAddressResolver; | ||||
| import org.asamk.signal.manager.storage.configuration.ConfigurationStore; | ||||
| import org.asamk.signal.manager.storage.configuration.LegacyConfigurationStore; | ||||
| import org.asamk.signal.manager.storage.contacts.ContactsStore; | ||||
| import org.asamk.signal.manager.storage.contacts.LegacyJsonContactsStore; | ||||
| import org.asamk.signal.manager.storage.groups.GroupInfoV1; | ||||
| @ -20,6 +21,7 @@ import org.asamk.signal.manager.storage.groups.LegacyGroupStore; | ||||
| import org.asamk.signal.manager.storage.identities.IdentityKeyStore; | ||||
| import org.asamk.signal.manager.storage.identities.LegacyIdentityKeyStore; | ||||
| import org.asamk.signal.manager.storage.identities.SignalIdentityKeyStore; | ||||
| import org.asamk.signal.manager.storage.keyValue.KeyValueStore; | ||||
| import org.asamk.signal.manager.storage.messageCache.MessageCache; | ||||
| import org.asamk.signal.manager.storage.prekeys.KyberPreKeyStore; | ||||
| import org.asamk.signal.manager.storage.prekeys.LegacyPreKeyStore; | ||||
| @ -151,7 +153,7 @@ public class SignalAccount implements Closeable { | ||||
|     private RecipientStore recipientStore; | ||||
|     private StickerStore stickerStore; | ||||
|     private ConfigurationStore configurationStore; | ||||
|     private ConfigurationStore.Storage configurationStoreStorage; | ||||
|     private KeyValueStore keyValueStore; | ||||
| 
 | ||||
|     private MessageCache messageCache; | ||||
|     private MessageSendLogStore messageSendLogStore; | ||||
| @ -216,7 +218,6 @@ public class SignalAccount implements Closeable { | ||||
|         signalAccount.aciAccountData.setLocalRegistrationId(registrationId); | ||||
|         signalAccount.pniAccountData.setLocalRegistrationId(pniRegistrationId); | ||||
|         signalAccount.settings = settings; | ||||
|         signalAccount.configurationStore = new ConfigurationStore(signalAccount::saveConfigurationStore); | ||||
| 
 | ||||
|         signalAccount.registered = false; | ||||
| 
 | ||||
| @ -266,8 +267,6 @@ public class SignalAccount implements Closeable { | ||||
|                 pniIdentityKey, | ||||
|                 profileKey); | ||||
| 
 | ||||
|         signalAccount.configurationStore = new ConfigurationStore(signalAccount::saveConfigurationStore); | ||||
| 
 | ||||
|         signalAccount.getRecipientTrustedResolver() | ||||
|                 .resolveSelfRecipientTrusted(signalAccount.getSelfRecipientAddress()); | ||||
|         signalAccount.previousStorageVersion = CURRENT_STORAGE_VERSION; | ||||
| @ -774,12 +773,10 @@ public class SignalAccount implements Closeable { | ||||
|         } | ||||
| 
 | ||||
|         if (rootNode.hasNonNull("configurationStore")) { | ||||
|             configurationStoreStorage = jsonProcessor.convertValue(rootNode.get("configurationStore"), | ||||
|                     ConfigurationStore.Storage.class); | ||||
|             configurationStore = ConfigurationStore.fromStorage(configurationStoreStorage, | ||||
|                     this::saveConfigurationStore); | ||||
|         } else { | ||||
|             configurationStore = new ConfigurationStore(this::saveConfigurationStore); | ||||
|             final var configurationStoreStorage = jsonProcessor.convertValue(rootNode.get("configurationStore"), | ||||
|                     LegacyConfigurationStore.Storage.class); | ||||
|             LegacyConfigurationStore.migrate(configurationStoreStorage, getConfigurationStore()); | ||||
|             migratedLegacyConfig = true; | ||||
|         } | ||||
| 
 | ||||
|         migratedLegacyConfig = loadLegacyThreadStore(rootNode) || migratedLegacyConfig; | ||||
| @ -965,11 +962,6 @@ public class SignalAccount implements Closeable { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     private void saveConfigurationStore(ConfigurationStore.Storage storage) { | ||||
|         this.configurationStoreStorage = storage; | ||||
|         save(); | ||||
|     } | ||||
| 
 | ||||
|     private void save() { | ||||
|         synchronized (fileChannel) { | ||||
|             var rootNode = jsonProcessor.createObjectNode(); | ||||
| @ -1028,8 +1020,7 @@ public class SignalAccount implements Closeable { | ||||
|                             pniAccountData.getPreKeyMetadata().activeLastResortKyberPreKeyId) | ||||
|                     .put("profileKey", | ||||
|                             profileKey == null ? null : Base64.getEncoder().encodeToString(profileKey.serialize())) | ||||
|                     .put("registered", registered) | ||||
|                     .putPOJO("configurationStore", configurationStoreStorage); | ||||
|                     .put("registered", registered); | ||||
|             try { | ||||
|                 try (var output = new ByteArrayOutputStream()) { | ||||
|                     // Write to memory first to prevent corrupting the file in case of serialization errors | ||||
| @ -1295,8 +1286,13 @@ public class SignalAccount implements Closeable { | ||||
|         return getOrCreate(() -> senderKeyStore, () -> senderKeyStore = new SenderKeyStore(getAccountDatabase())); | ||||
|     } | ||||
| 
 | ||||
|     private KeyValueStore getKeyValueStore() { | ||||
|         return getOrCreate(() -> keyValueStore, () -> keyValueStore = new KeyValueStore(getAccountDatabase())); | ||||
|     } | ||||
| 
 | ||||
|     public ConfigurationStore getConfigurationStore() { | ||||
|         return configurationStore; | ||||
|         return getOrCreate(() -> configurationStore, | ||||
|                 () -> configurationStore = new ConfigurationStore(getKeyValueStore())); | ||||
|     } | ||||
| 
 | ||||
|     public MessageCache getMessageCache() { | ||||
| @ -1662,7 +1658,7 @@ public class SignalAccount implements Closeable { | ||||
|     } | ||||
| 
 | ||||
|     public boolean isDiscoverableByPhoneNumber() { | ||||
|         final var phoneNumberUnlisted = configurationStore.getPhoneNumberUnlisted(); | ||||
|         final var phoneNumberUnlisted = getConfigurationStore().getPhoneNumberUnlisted(); | ||||
|         return phoneNumberUnlisted == null || !phoneNumberUnlisted; | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -1,106 +1,75 @@ | ||||
| package org.asamk.signal.manager.storage.configuration; | ||||
| 
 | ||||
| import org.asamk.signal.manager.api.PhoneNumberSharingMode; | ||||
| import org.asamk.signal.manager.storage.keyValue.KeyValueEntry; | ||||
| import org.asamk.signal.manager.storage.keyValue.KeyValueStore; | ||||
| 
 | ||||
| public class ConfigurationStore { | ||||
| 
 | ||||
|     private final Saver saver; | ||||
|     private final KeyValueStore keyValueStore; | ||||
| 
 | ||||
|     private Boolean readReceipts; | ||||
|     private Boolean unidentifiedDeliveryIndicators; | ||||
|     private Boolean typingIndicators; | ||||
|     private Boolean linkPreviews; | ||||
|     private Boolean phoneNumberUnlisted; | ||||
|     private PhoneNumberSharingMode phoneNumberSharingMode; | ||||
|     private final KeyValueEntry<Boolean> readReceipts = new KeyValueEntry<>("config-read-receipts", Boolean.class); | ||||
|     private final KeyValueEntry<Boolean> unidentifiedDeliveryIndicators = new KeyValueEntry<>( | ||||
|             "config-unidentified-delivery-indicators", | ||||
|             Boolean.class); | ||||
|     private final KeyValueEntry<Boolean> typingIndicators = new KeyValueEntry<>("config-typing-indicators", | ||||
|             Boolean.class); | ||||
|     private final KeyValueEntry<Boolean> linkPreviews = new KeyValueEntry<>("config-link-previews", Boolean.class); | ||||
|     private final KeyValueEntry<Boolean> phoneNumberUnlisted = new KeyValueEntry<>("config-phone-number-unlisted", | ||||
|             Boolean.class); | ||||
|     private final KeyValueEntry<PhoneNumberSharingMode> phoneNumberSharingMode = new KeyValueEntry<>( | ||||
|             "config-phone-number-sharing-mode", | ||||
|             PhoneNumberSharingMode.class); | ||||
| 
 | ||||
|     public ConfigurationStore(final Saver saver) { | ||||
|         this.saver = saver; | ||||
|     } | ||||
| 
 | ||||
|     public static ConfigurationStore fromStorage(Storage storage, Saver saver) { | ||||
|         final var store = new ConfigurationStore(saver); | ||||
|         store.readReceipts = storage.readReceipts; | ||||
|         store.unidentifiedDeliveryIndicators = storage.unidentifiedDeliveryIndicators; | ||||
|         store.typingIndicators = storage.typingIndicators; | ||||
|         store.linkPreviews = storage.linkPreviews; | ||||
|         store.phoneNumberSharingMode = storage.phoneNumberSharingMode; | ||||
|         return store; | ||||
|     public ConfigurationStore(final KeyValueStore keyValueStore) { | ||||
|         this.keyValueStore = keyValueStore; | ||||
|     } | ||||
| 
 | ||||
|     public Boolean getReadReceipts() { | ||||
|         return readReceipts; | ||||
|         return keyValueStore.getEntry(readReceipts); | ||||
|     } | ||||
| 
 | ||||
|     public void setReadReceipts(final boolean readReceipts) { | ||||
|         this.readReceipts = readReceipts; | ||||
|         saver.save(toStorage()); | ||||
|     public void setReadReceipts(final boolean value) { | ||||
|         keyValueStore.storeEntry(readReceipts, value); | ||||
|     } | ||||
| 
 | ||||
|     public Boolean getUnidentifiedDeliveryIndicators() { | ||||
|         return unidentifiedDeliveryIndicators; | ||||
|         return keyValueStore.getEntry(unidentifiedDeliveryIndicators); | ||||
|     } | ||||
| 
 | ||||
|     public void setUnidentifiedDeliveryIndicators(final boolean unidentifiedDeliveryIndicators) { | ||||
|         this.unidentifiedDeliveryIndicators = unidentifiedDeliveryIndicators; | ||||
|         saver.save(toStorage()); | ||||
|     public void setUnidentifiedDeliveryIndicators(final boolean value) { | ||||
|         keyValueStore.storeEntry(unidentifiedDeliveryIndicators, value); | ||||
|     } | ||||
| 
 | ||||
|     public Boolean getTypingIndicators() { | ||||
|         return typingIndicators; | ||||
|         return keyValueStore.getEntry(typingIndicators); | ||||
|     } | ||||
| 
 | ||||
|     public void setTypingIndicators(final boolean typingIndicators) { | ||||
|         this.typingIndicators = typingIndicators; | ||||
|         saver.save(toStorage()); | ||||
|     public void setTypingIndicators(final boolean value) { | ||||
|         keyValueStore.storeEntry(typingIndicators, value); | ||||
|     } | ||||
| 
 | ||||
|     public Boolean getLinkPreviews() { | ||||
|         return linkPreviews; | ||||
|         return keyValueStore.getEntry(linkPreviews); | ||||
|     } | ||||
| 
 | ||||
|     public void setLinkPreviews(final boolean linkPreviews) { | ||||
|         this.linkPreviews = linkPreviews; | ||||
|         saver.save(toStorage()); | ||||
|     public void setLinkPreviews(final boolean value) { | ||||
|         keyValueStore.storeEntry(linkPreviews, value); | ||||
|     } | ||||
| 
 | ||||
|     public Boolean getPhoneNumberUnlisted() { | ||||
|         return phoneNumberUnlisted; | ||||
|         return keyValueStore.getEntry(phoneNumberUnlisted); | ||||
|     } | ||||
| 
 | ||||
|     public void setPhoneNumberUnlisted(final boolean phoneNumberUnlisted) { | ||||
|         this.phoneNumberUnlisted = phoneNumberUnlisted; | ||||
|         saver.save(toStorage()); | ||||
|     public void setPhoneNumberUnlisted(final boolean value) { | ||||
|         keyValueStore.storeEntry(phoneNumberUnlisted, value); | ||||
|     } | ||||
| 
 | ||||
|     public PhoneNumberSharingMode getPhoneNumberSharingMode() { | ||||
|         return phoneNumberSharingMode; | ||||
|         return keyValueStore.getEntry(phoneNumberSharingMode); | ||||
|     } | ||||
| 
 | ||||
|     public void setPhoneNumberSharingMode(final PhoneNumberSharingMode phoneNumberSharingMode) { | ||||
|         this.phoneNumberSharingMode = phoneNumberSharingMode; | ||||
|         saver.save(toStorage()); | ||||
|     } | ||||
| 
 | ||||
|     private Storage toStorage() { | ||||
|         return new Storage(readReceipts, | ||||
|                 unidentifiedDeliveryIndicators, | ||||
|                 typingIndicators, | ||||
|                 linkPreviews, | ||||
|                 phoneNumberUnlisted, | ||||
|                 phoneNumberSharingMode); | ||||
|     } | ||||
| 
 | ||||
|     public record Storage( | ||||
|             Boolean readReceipts, | ||||
|             Boolean unidentifiedDeliveryIndicators, | ||||
|             Boolean typingIndicators, | ||||
|             Boolean linkPreviews, | ||||
|             Boolean phoneNumberUnlisted, | ||||
|             PhoneNumberSharingMode phoneNumberSharingMode | ||||
|     ) {} | ||||
| 
 | ||||
|     public interface Saver { | ||||
| 
 | ||||
|         void save(Storage storage); | ||||
|     public void setPhoneNumberSharingMode(final PhoneNumberSharingMode value) { | ||||
|         keyValueStore.storeEntry(phoneNumberSharingMode, value); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -0,0 +1,33 @@ | ||||
| package org.asamk.signal.manager.storage.configuration; | ||||
| 
 | ||||
| import org.asamk.signal.manager.api.PhoneNumberSharingMode; | ||||
| 
 | ||||
| public class LegacyConfigurationStore { | ||||
| 
 | ||||
|     public static void migrate(Storage storage, ConfigurationStore configurationStore) { | ||||
|         if (storage.readReceipts != null) { | ||||
|             configurationStore.setReadReceipts(storage.readReceipts); | ||||
|         } | ||||
|         if (storage.unidentifiedDeliveryIndicators != null) { | ||||
|             configurationStore.setUnidentifiedDeliveryIndicators(storage.unidentifiedDeliveryIndicators); | ||||
|         } | ||||
|         if (storage.typingIndicators != null) { | ||||
|             configurationStore.setTypingIndicators(storage.typingIndicators); | ||||
|         } | ||||
|         if (storage.linkPreviews != null) { | ||||
|             configurationStore.setLinkPreviews(storage.linkPreviews); | ||||
|         } | ||||
|         if (storage.phoneNumberSharingMode != null) { | ||||
|             configurationStore.setPhoneNumberSharingMode(storage.phoneNumberSharingMode); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public record Storage( | ||||
|             Boolean readReceipts, | ||||
|             Boolean unidentifiedDeliveryIndicators, | ||||
|             Boolean typingIndicators, | ||||
|             Boolean linkPreviews, | ||||
|             Boolean phoneNumberUnlisted, | ||||
|             PhoneNumberSharingMode phoneNumberSharingMode | ||||
|     ) {} | ||||
| } | ||||
| @ -0,0 +1,8 @@ | ||||
| package org.asamk.signal.manager.storage.keyValue; | ||||
| 
 | ||||
| public record KeyValueEntry<T>(String key, Class<T> clazz, T defaultValue) { | ||||
| 
 | ||||
|     public KeyValueEntry(String key, Class<T> clazz) { | ||||
|         this(key, clazz, null); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,153 @@ | ||||
| package org.asamk.signal.manager.storage.keyValue; | ||||
| 
 | ||||
| import org.asamk.signal.manager.storage.Database; | ||||
| import org.asamk.signal.manager.storage.Utils; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| 
 | ||||
| import java.sql.Connection; | ||||
| import java.sql.PreparedStatement; | ||||
| import java.sql.ResultSet; | ||||
| import java.sql.SQLException; | ||||
| import java.sql.Types; | ||||
| 
 | ||||
| public class KeyValueStore { | ||||
| 
 | ||||
|     private static final String TABLE_KEY_VALUE = "key_value"; | ||||
|     private final static Logger logger = LoggerFactory.getLogger(KeyValueStore.class); | ||||
| 
 | ||||
|     private final Database database; | ||||
| 
 | ||||
|     public static void createSql(Connection connection) throws SQLException { | ||||
|         // When modifying the CREATE statement here, also add a migration in AccountDatabase.java | ||||
|         try (final var statement = connection.createStatement()) { | ||||
|             statement.executeUpdate(""" | ||||
|                                     CREATE TABLE key_value ( | ||||
|                                       _id INTEGER PRIMARY KEY, | ||||
|                                       key TEXT UNIQUE NOT NULL, | ||||
|                                       value ANY | ||||
|                                     ) STRICT; | ||||
|                                     """); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public KeyValueStore(final Database database) { | ||||
|         this.database = database; | ||||
|     } | ||||
| 
 | ||||
|     public <T> T getEntry(KeyValueEntry<T> key) { | ||||
|         final var sql = ( | ||||
|                 """ | ||||
|                 SELECT key, value | ||||
|                 FROM %s p | ||||
|                 WHERE p.key = ? | ||||
|                 """ | ||||
|         ).formatted(TABLE_KEY_VALUE); | ||||
|         try (final var connection = database.getConnection()) { | ||||
|             try (final var statement = connection.prepareStatement(sql)) { | ||||
|                 statement.setString(1, key.key()); | ||||
| 
 | ||||
|                 final var result = Utils.executeQueryForOptional(statement, | ||||
|                         resultSet -> readValueFromResultSet(key, resultSet)).orElse(null); | ||||
| 
 | ||||
|                 if (result == null) { | ||||
|                     return key.defaultValue(); | ||||
|                 } | ||||
|                 return result; | ||||
|             } | ||||
|         } catch (SQLException e) { | ||||
|             throw new RuntimeException("Failed read from pre_key store", e); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public <T> void storeEntry(KeyValueEntry<T> key, T value) { | ||||
|         final var sql = ( | ||||
|                 """ | ||||
|                 INSERT INTO %s (key, value) | ||||
|                 VALUES (?1, ?2) | ||||
|                 ON CONFLICT (key) DO UPDATE SET value=excluded.value | ||||
|                 """ | ||||
|         ).formatted(TABLE_KEY_VALUE); | ||||
|         try (final var connection = database.getConnection()) { | ||||
|             try (final var statement = connection.prepareStatement(sql)) { | ||||
|                 statement.setString(1, key.key()); | ||||
|                 setParameterValue(statement, 2, key.clazz(), value); | ||||
|                 statement.executeUpdate(); | ||||
|             } | ||||
|         } catch (SQLException e) { | ||||
|             throw new RuntimeException("Failed update key_value store", e); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private static <T> T readValueFromResultSet( | ||||
|             final KeyValueEntry<T> key, final ResultSet resultSet | ||||
|     ) throws SQLException { | ||||
|         Object value; | ||||
|         final var clazz = key.clazz(); | ||||
|         if (clazz == int.class || clazz == Integer.class) { | ||||
|             value = resultSet.getInt("value"); | ||||
|         } else if (clazz == long.class || clazz == Long.class) { | ||||
|             value = resultSet.getLong("value"); | ||||
|         } else if (clazz == boolean.class || clazz == Boolean.class) { | ||||
|             value = resultSet.getBoolean("value"); | ||||
|         } else if (clazz == String.class) { | ||||
|             value = resultSet.getString("value"); | ||||
|         } else if (Enum.class.isAssignableFrom(clazz)) { | ||||
|             final var name = resultSet.getString("value"); | ||||
|             if (name == null) { | ||||
|                 value = null; | ||||
|             } else { | ||||
|                 try { | ||||
|                     value = Enum.valueOf((Class<Enum>) key.clazz(), name); | ||||
|                 } catch (IllegalArgumentException e) { | ||||
|                     logger.debug("Read invalid enum value from store, ignoring: {} for {}", name, key.clazz()); | ||||
|                     value = null; | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             throw new AssertionError("Invalid key type " + clazz.getSimpleName()); | ||||
|         } | ||||
|         if (resultSet.wasNull()) { | ||||
|             return null; | ||||
|         } | ||||
|         return (T) value; | ||||
|     } | ||||
| 
 | ||||
|     private static <T> void setParameterValue( | ||||
|             final PreparedStatement statement, final int parameterIndex, final Class<T> clazz, final T value | ||||
|     ) throws SQLException { | ||||
|         if (clazz == int.class || clazz == Integer.class) { | ||||
|             if (value == null) { | ||||
|                 statement.setNull(parameterIndex, Types.INTEGER); | ||||
|             } else { | ||||
|                 statement.setInt(parameterIndex, (int) value); | ||||
|             } | ||||
|         } else if (clazz == long.class || clazz == Long.class) { | ||||
|             if (value == null) { | ||||
|                 statement.setNull(parameterIndex, Types.INTEGER); | ||||
|             } else { | ||||
|                 statement.setLong(parameterIndex, (long) value); | ||||
|             } | ||||
|         } else if (clazz == boolean.class || clazz == Boolean.class) { | ||||
|             if (value == null) { | ||||
|                 statement.setNull(parameterIndex, Types.BOOLEAN); | ||||
|             } else { | ||||
|                 statement.setBoolean(parameterIndex, (boolean) value); | ||||
|             } | ||||
|         } else if (clazz == String.class) { | ||||
|             if (value == null) { | ||||
|                 statement.setNull(parameterIndex, Types.VARCHAR); | ||||
|             } else { | ||||
|                 statement.setString(parameterIndex, (String) value); | ||||
|             } | ||||
|         } else if (Enum.class.isAssignableFrom(clazz)) { | ||||
|             if (value == null) { | ||||
|                 statement.setNull(parameterIndex, Types.VARCHAR); | ||||
|             } else { | ||||
|                 statement.setString(parameterIndex, ((Enum<?>) value).name()); | ||||
|             } | ||||
|         } else { | ||||
|             throw new AssertionError("Invalid key type " + clazz.getSimpleName()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 AsamK
						AsamK