diff --git a/example/lib/task.dart b/example/lib/task.dart index 4e9c8bd8..747240b4 100644 --- a/example/lib/task.dart +++ b/example/lib/task.dart @@ -7,7 +7,10 @@ class Task { final String message; - Task(this.id, this.message); + @transient + String description; + + Task(this.id, this.message, [this.description='']); @override bool operator ==(Object other) => diff --git a/floor_annotation/lib/floor_annotation.dart b/floor_annotation/lib/floor_annotation.dart index 760a38d9..f7366eb7 100644 --- a/floor_annotation/lib/floor_annotation.dart +++ b/floor_annotation/lib/floor_annotation.dart @@ -12,4 +12,5 @@ export 'src/on_conflict_strategy.dart'; export 'src/primary_key.dart'; export 'src/query.dart'; export 'src/transaction.dart'; +export 'src/transient.dart'; export 'src/update.dart'; diff --git a/floor_annotation/lib/src/transient.dart b/floor_annotation/lib/src/transient.dart new file mode 100644 index 00000000..ae11ef39 --- /dev/null +++ b/floor_annotation/lib/src/transient.dart @@ -0,0 +1,7 @@ +/// Marks a field in an [Entity] as the transient. +class Transient { + const Transient(); +} + +/// Marks a field in an [Entity] as the transient. +const transient = Transient(); diff --git a/floor_generator/lib/processor/entity_processor.dart b/floor_generator/lib/processor/entity_processor.dart index 1f80fb4f..cb079a5b 100644 --- a/floor_generator/lib/processor/entity_processor.dart +++ b/floor_generator/lib/processor/entity_processor.dart @@ -30,16 +30,16 @@ class EntityProcessor extends Processor { @override Entity process() { final name = _getName(); - final fields = _getFields(); + final allFieldsButTransients = _getAllButTransientsFields(); return Entity( _classElement, name, - fields, - _getPrimaryKey(fields), + allFieldsButTransients, + _getPrimaryKey(allFieldsButTransients), _getForeignKeys(), - _getIndices(fields, name), - _getConstructor(fields), + _getIndices(allFieldsButTransients, name), + _getConstructor(allFieldsButTransients), ); } @@ -53,9 +53,9 @@ class EntityProcessor extends Processor { } @nonNull - List _getFields() { + List _getAllButTransientsFields() { return _classElement.fields - .where(_isNotHashCode) + .where(_isNotHashCode).where(_isNotTransient) .map((field) => FieldProcessor(field).process()) .toList(); } @@ -65,6 +65,12 @@ class EntityProcessor extends Processor { return fieldElement.displayName != 'hashCode'; } + @nonNull + bool _isNotTransient(final FieldElement fieldElement) { + return !typeChecker(annotations.Transient) + .hasAnnotationOfExact(fieldElement); + } + @nonNull List _getForeignKeys() { return _entityTypeChecker @@ -231,7 +237,7 @@ class EntityProcessor extends Processor { @nonNull String _getConstructor(final List fields) { final columnNames = fields.map((field) => field.columnName).toList(); - final constructorParameters = _classElement.constructors.first.parameters; + final constructorParameters = _classElement.constructors.first.parameters. where((f)=> columnNames.contains(f.name)).toList(); final parameterValues = []; diff --git a/floor_generator/lib/value_object/transient.dart b/floor_generator/lib/value_object/transient.dart new file mode 100644 index 00000000..f0e4787a --- /dev/null +++ b/floor_generator/lib/value_object/transient.dart @@ -0,0 +1,21 @@ +import 'package:collection/collection.dart'; +import 'package:floor_generator/value_object/field.dart'; + +/// Transient representation of a field in an Entity +class Transient { + final List fields; + + Transient(this.fields); + + @override + bool operator ==(Object other) => + identical(this, other) || + other is Transient && + runtimeType == other.runtimeType && + const ListEquality().equals(fields, other.fields); + + @override + String toString() { + return 'Transient{fields: $fields}'; + } +} diff --git a/floor_generator/test/processor/entity_processor_test.dart b/floor_generator/test/processor/entity_processor_test.dart index 510ed7c3..27da2326 100644 --- a/floor_generator/test/processor/entity_processor_test.dart +++ b/floor_generator/test/processor/entity_processor_test.dart @@ -128,6 +128,45 @@ void main() { expect(actual, equals(expected)); }); }); + + test('Should entity with transient field be not equals to floor processed entity', () async { + final classElement = await _createClassElement(''' + @entity + class Person { + @primaryKey + final int id; + + final String name; + + @transient + String nickName; + + Person(this.id, this.name, [this.nickName='']); + } + '''); + + final actual = EntityProcessor(classElement).process(); + + const name = 'Person'; + final fields = classElement.fields + .map((fieldElement) => FieldProcessor(fieldElement).process()) + .toList(); + final primaryKey = PrimaryKey([fields[0]], false); + const foreignKeys = []; + const indices = []; + const constructor = "Person(row['id'] as int, row['name'] as String)"; + final expected = Entity( + classElement, + name, + fields, + primaryKey, + foreignKeys, + indices, + constructor, + ); + + expect(actual.fields, isNot(equals(expected.fields))); + }); } Future _createClassElement(final String clazz) async { diff --git a/floor_test/test/database_test.dart b/floor_test/test/database_test.dart index 717cfaf2..dd14af8a 100644 --- a/floor_test/test/database_test.dart +++ b/floor_test/test/database_test.dart @@ -45,7 +45,7 @@ void main() { group('change single item', () { test('insert person', () async { - final person = Person(null, 'Simon'); + final person = Person(null, 'Rick','Rock', 'Simon'); await personDao.insertPerson(person); final actual = await personDao.findAllPersons(); @@ -54,7 +54,7 @@ void main() { }); test('delete person', () async { - final person = Person(1, 'Simon'); + final person = Person(1,'Rick','Rock', 'Simon'); await personDao.insertPerson(person); await personDao.deletePerson(person); @@ -64,9 +64,9 @@ void main() { }); test('update person', () async { - final person = Person(1, 'Simon'); + final person = Person(1,'Rick','Rock', 'Simon'); await personDao.insertPerson(person); - final updatedPerson = Person(person.id, _reverse(person.name)); + final updatedPerson = Person(person.id,_reverse(person.nickName),_reverse(person.alias), _reverse(person.name)); await personDao.updatePerson(updatedPerson); @@ -77,7 +77,7 @@ void main() { group('change multiple items', () { test('insert persons', () async { - final persons = [Person(1, 'Simon'), Person(2, 'Frank')]; + final persons = [Person(1,'Rick','Rock', 'Simon'), Person(2,'Joe','Vallery', 'Samuel')]; await personDao.insertPersons(persons); @@ -86,7 +86,7 @@ void main() { }); test('delete persons', () async { - final persons = [Person(1, 'Simon'), Person(2, 'Frank')]; + final persons = [Person(1,'Rick','Rock', 'Simon'), Person(2,'Joe','Vallery', 'Samuel')]; await personDao.insertPersons(persons); await personDao.deletePersons(persons); @@ -96,10 +96,10 @@ void main() { }); test('update persons', () async { - final persons = [Person(1, 'Simon'), Person(2, 'Frank')]; + final persons = [Person(1,'Rick','Rock', 'Simon'), Person(2,'Joe','Vallery', 'Samuel')]; await personDao.insertPersons(persons); final updatedPersons = persons - .map((person) => Person(person.id, _reverse(person.name))) + .map((person) => Person(person.id,_reverse(person.nickName),_reverse(person.alias), _reverse(person.name))) .toList(); await personDao.updatePersons(updatedPersons); @@ -111,7 +111,7 @@ void main() { group('querying', () { test('query with two parameters (int, String)', () async { - final person = Person(1, 'Frank'); + final person = Person(1,'Rick','Rock', 'Simon'); await personDao.insertPerson(person); final actual = await personDao.findPersonByIdAndName(1, 'Frank'); @@ -122,9 +122,9 @@ void main() { group('transaction', () { test('replace persons in transaction', () async { - final persons = [Person(1, 'Simon'), Person(2, 'Frank')]; + final persons = [Person(1,'Rick','Rock', 'Simon'), Person(2,'Joe','Vallery', 'Samuel')]; await personDao.insertPersons(persons); - final newPersons = [Person(3, 'Paul'), Person(4, 'Karl')]; + final newPersons =[Person(3,'Mickey','Wick', 'Paul'), Person(4,'Raul','Nox', 'Karl')]; await personDao.replacePersons(newPersons); @@ -133,9 +133,9 @@ void main() { }); test('Reactivity is retained when using transactions', () async { - final persons = [Person(1, 'Simon'), Person(2, 'Frank')]; + final persons = [Person(1,'Rick','Rock', 'Simon'), Person(2,'Joe','Vallery', 'Samuel')]; await personDao.insertPersons(persons); - final newPersons = [Person(3, 'Paul'), Person(4, 'Karl')]; + final newPersons =[Person(3,'Mickey','Wick', 'Paul'), Person(4,'Raul','Nox', 'Karl')]; final actual = personDao.findAllPersonsAsStream(); expect(actual, emits(persons)); @@ -147,7 +147,7 @@ void main() { group('change items and return int/list of int', () { test('insert person and return id of inserted item', () async { - final person = Person(1, 'Simon'); + final person = Person(1,'Rick','Rock', 'Simon'); final actual = await personDao.insertPersonWithReturn(person); @@ -155,7 +155,7 @@ void main() { }); test('insert persons and return ids of inserted items', () async { - final persons = [Person(1, 'Simon'), Person(2, 'Frank')]; + final persons = [Person(1,'Rick','Rock', 'Simon'), Person(2,'Joe','Vallery', 'Samuel')]; final actual = await personDao.insertPersonsWithReturn(persons); @@ -164,9 +164,9 @@ void main() { }); test('update person and return 1 (affected row count)', () async { - final person = Person(1, 'Simon'); + final person = Person(1,'Rick','Rock', 'Simon'); await personDao.insertPerson(person); - final updatedPerson = Person(person.id, _reverse(person.name)); + final updatedPerson = Person(person.id,_reverse(person.nickName),_reverse(person.alias), _reverse(person.name)); final actual = await personDao.updatePersonWithReturn(updatedPerson); @@ -176,10 +176,10 @@ void main() { }); test('update persons and return affected rows count', () async { - final persons = [Person(1, 'Simon'), Person(2, 'Frank')]; + final persons = [Person(1,'Rick','Rock', 'Simon'), Person(2,'Joe','Vallery', 'Samuel')]; await personDao.insertPersons(persons); final updatedPersons = persons - .map((person) => Person(person.id, _reverse(person.name))) + .map((person) => Person(person.id,_reverse(person.nickName),_reverse(person.alias), _reverse(person.name))) .toList(); final actual = await personDao.updatePersonsWithReturn(updatedPersons); @@ -190,7 +190,7 @@ void main() { }); test('delete person and return 1 (affected row count)', () async { - final person = Person(1, 'Simon'); + final person = Person(1,'Rick','Rock', 'Simon'); await personDao.insertPerson(person); final actual = await personDao.deletePersonWithReturn(person); @@ -199,7 +199,7 @@ void main() { }); test('delete persons and return affected rows count', () async { - final persons = [Person(1, 'Simon'), Person(2, 'Frank')]; + final persons = [Person(1,'Rick','Rock', 'Simon'), Person(2,'Joe','Vallery', 'Samuel')]; await personDao.insertPersons(persons); final actual = await personDao.deletePersonsWithReturn(persons); @@ -210,15 +210,15 @@ void main() { group('foreign key', () { test('foreign key constraint failed exception', () { - final dog = Dog(null, 'Peter', 'Pete', 2); + final dog = Dog(null,'Brown','Fido', 'Peter', 'Pete', 2); expect(() => dogDao.insertDog(dog), _throwsDatabaseException); }); test('find dog for person', () async { - final person = Person(1, 'Simon'); + final person = Person(1,'Rick','Rock', 'Simon'); await personDao.insertPerson(person); - final dog = Dog(2, 'Peter', 'Pete', person.id); + final dog = Dog(2,'Brown' ,'Fido','Peter', 'Pete', person.id); await dogDao.insertDog(dog); final actual = await dogDao.findDogForPersonId(person.id); @@ -227,9 +227,9 @@ void main() { }); test('cascade delete dog on deletion of person', () async { - final person = Person(1, 'Simon'); + final person = Person(1,'Rick','Rock', 'Simon'); await personDao.insertPerson(person); - final dog = Dog(2, 'Peter', 'Pete', person.id); + final dog = Dog(2,'Brown' ,'Fido','Peter', 'Pete', person.id); await dogDao.insertDog(dog); await personDao.deletePerson(person); @@ -241,7 +241,7 @@ void main() { group('query with void return', () { test('delete all persons', () async { - final persons = [Person(1, 'Simon'), Person(2, 'Frank')]; + final persons = [Person(1,'Rick','Rock', 'Simon'), Person(2,'Joe','Vallery', 'Samuel')]; await personDao.insertPersons(persons); await personDao.deleteAllPersons(); @@ -253,7 +253,7 @@ void main() { group('stream queries', () { test('initially emit persistent data', () async { - final person = Person(1, 'Simon'); + final person = Person(1,'Rick','Rock', 'Simon'); await personDao.insertPerson(person); final actual = personDao.findAllPersonsAsStream(); @@ -263,7 +263,7 @@ void main() { group('insert change', () { test('find person by id as stream', () async { - final person = Person(1, 'Simon'); + final person = Person(1,'Rick','Rock', 'Simon'); final actual = personDao.findPersonByIdAsStream(person.id); @@ -272,7 +272,7 @@ void main() { }); test('find all persons as stream', () async { - final persons = [Person(1, 'Simon'), Person(2, 'Frank')]; + final persons = [Person(1,'Rick','Rock', 'Simon'), Person(2,'Joe','Vallery', 'Samuel')]; final actual = personDao.findAllPersonsAsStream(); expect(actual, emits(>[])); @@ -282,8 +282,8 @@ void main() { }); test('initially emits persistent data then new', () async { - final persons = [Person(1, 'Simon'), Person(2, 'Frank')]; - final persons2 = [Person(3, 'Paul'), Person(4, 'George')]; + final persons = [Person(1,'Rick','Rock', 'Simon'), Person(2,'Joe','Vallery', 'Samuel')]; + final persons2 =[Person(3,'Mickey','Wick', 'Paul'), Person(4,'Raul','Nox', 'Karl')]; await personDao.insertPersons(persons); final actual = personDao.findAllPersonsAsStream(); @@ -296,21 +296,21 @@ void main() { group('update change', () { test('update item', () async { - final person = Person(1, 'Simon'); + final person = Person(1,'Rick','Rock', 'Simon'); await personDao.insertPerson(person); final actual = personDao.findAllPersonsAsStream(); expect(actual, emits([person])); - final updatedPerson = Person(person.id, 'Frank'); + final updatedPerson = Person(person.id,'Rick','Rock', 'Frank'); await personDao.updatePerson(updatedPerson); expect(actual, emits([updatedPerson])); }); test('update items', () async { - final persons = [Person(1, 'Simon'), Person(2, 'Frank')]; + final persons = [Person(1,'Rick','Rock', 'Simon'), Person(2,'Joe','Vallery', 'Samuel')]; final updatedPersons = persons - .map((person) => Person(person.id, _reverse(person.name))) + .map((person) => Person(person.id, _reverse(person.nickName),_reverse(person.alias),_reverse(person.name))) .toList(); await personDao.insertPersons(persons); @@ -324,7 +324,7 @@ void main() { group('deletion change', () { test('delete item', () async { - final person = Person(1, 'Simon'); + final person = Person(1,'Rick','Rock', 'Simon'); await personDao.insertPerson(person); final actual = personDao.findAllPersonsAsStream(); @@ -335,7 +335,7 @@ void main() { }); test('delete items', () async { - final persons = [Person(1, 'Simon'), Person(2, 'Frank')]; + final persons = [Person(1,'Rick','Rock', 'Simon'), Person(2,'Joe','Vallery', 'Samuel')]; await personDao.insertPersons(persons); final actual = personDao.findAllPersonsAsStream(); @@ -349,9 +349,9 @@ void main() { group('IN clause', () { test('Find persons with IDs', () async { - final person1 = Person(1, 'Simon'); - final person2 = Person(2, 'Frank'); - final person3 = Person(3, 'Paul'); + final person1 = Person(1,'Rick','Rock', 'Simon'); + final person2 =Person(2,'Joe','Vallery', 'Samuel'); + final person3 = Person(3,'Mickey','Wick', 'Paul'); await personDao.insertPersons([person1, person2, person3]); final ids = [person1.id, person2.id]; @@ -361,9 +361,9 @@ void main() { }); test('Find persons with names', () async { - final person1 = Person(1, 'Simon'); - final person2 = Person(2, 'Simon'); - final person3 = Person(3, 'Paul'); + final person1 = Person(1,'Rick','Rock', 'Simon'); + final person2 =Person(2,'Joe','Vallery', 'Samuel'); + final person3 = Person(3,'Mickey','Wick', 'Paul'); await personDao.insertPersons([person1, person2, person3]); final names = [person1.name, person2.name]; @@ -376,9 +376,9 @@ void main() { group('LIKE operator', () { test('Find persons with name LIKE', () async { final persons = [ - Person(1, 'Simon'), - Person(2, 'Frank'), - Person(3, 'Paul') + Person(1,'Rick','Rock', 'Simon'), + Person(2,'Joe','Vallery', 'Samuel'), + Person(3,'Mickey','Wick', 'Paul') ]; await personDao.insertPersons(persons); diff --git a/floor_test/test/model/dog.dart b/floor_test/test/model/dog.dart index 35066a39..30c0c132 100644 --- a/floor_test/test/model/dog.dart +++ b/floor_test/test/model/dog.dart @@ -17,6 +17,12 @@ class Dog { @primaryKey final int id; + @transient + final String color; + + @transient + final String alias; + final String name; @ColumnInfo(name: 'nick_name') @@ -25,7 +31,7 @@ class Dog { @ColumnInfo(name: 'owner_id') final int ownerId; - Dog(this.id, this.name, this.nickName, this.ownerId); + Dog(this.id,this.color ,this.alias, this.name, this.nickName, this.ownerId); @override bool operator ==(Object other) => @@ -33,16 +39,18 @@ class Dog { other is Dog && runtimeType == other.runtimeType && id == other.id && + color == other.color && + alias == other.alias && name == other.name && nickName == other.nickName && ownerId == other.ownerId; @override int get hashCode => - id.hashCode ^ name.hashCode ^ nickName.hashCode ^ ownerId.hashCode; + id.hashCode ^ color.hashCode ^ alias.hashCode ^ name.hashCode ^ nickName.hashCode ^ ownerId.hashCode; @override String toString() { - return 'Dog{id: $id, name: $name, nickName: $nickName, ownerId: $ownerId}'; + return 'Dog{id: $id, color: $color, alias: $alias, name: $name, nickName: $nickName, ownerId: $ownerId}'; } } diff --git a/floor_test/test/model/person.dart b/floor_test/test/model/person.dart index 817f9aa9..03e9841c 100644 --- a/floor_test/test/model/person.dart +++ b/floor_test/test/model/person.dart @@ -10,10 +10,17 @@ class Person { @primaryKey final int id; + @transient + final String nickName; + + @transient + final String alias; + + @ColumnInfo(name: 'custom_name', nullable: false) final String name; - Person(this.id, this.name); + Person(this.id,this.nickName,this.alias, this.name); @override bool operator ==(Object other) => @@ -21,13 +28,15 @@ class Person { other is Person && runtimeType == other.runtimeType && id == other.id && - name == other.name; + nickName == other.nickName && + alias == other.alias && + name == other.name ; @override - int get hashCode => id.hashCode ^ name.hashCode; + int get hashCode => id.hashCode ^ nickName.hashCode ^ alias.hashCode ^ name.hashCode; @override String toString() { - return 'Person{id: $id, name: $name}'; + return 'Person{id: $id, nickName: $nickName, alias: $alias, name: $name}'; } }