Database handling with room

83
Database handling with Room CC4.0 Kathleen Cole

Transcript of Database handling with room

Database handlingwith

Room

CC4.0 Kathleen Cole

About me

Sergi Martínez@sergiandreplace

Android GDE & dev at Schibsted Spain

Disclaimer:

All examples are in Kotlin

What is Room?

A library to handle android databases via an abstraction

layer

Tl,dr;

An ORM

Components

EntityA class representing a

database row

DAOAn interface defining

how to access to db

DatabaseHolds definition for

DAOs and Entities

Before anything elserepositories { jcenter() maven { url 'https://maven.google.com' }}

def room = '1.0.0-beta2'

implementation "android.arch.persistence.room:runtime:${room}"kapt "android.arch.persistence.room:compiler:${room}"

Entities

Simple entity

@Entity(tableName = "products")class ProductEntity( @PrimaryKey() @ColumnInfo(name = "id") var id: Long, @ColumnInfo(name = "name") var name: String, @ColumnInfo(name = "description") var description: String, @ColumnInfo(name = "qty") var quantity: Long)

Simple entity

@Entity(tableName = "products")class ProductEntity( @PrimaryKey() @ColumnInfo(name = "id") var id: Long, @ColumnInfo(name = "name") var name: String, @ColumnInfo(name = "description") var description: String, @ColumnInfo(name = "qty") var quantity: Long)

Simple entity

@Entity(tableName = "products")class ProductEntity( @PrimaryKey() @ColumnInfo(name = "id") var id: Long, @ColumnInfo(name = "name") var name: String, @ColumnInfo(name = "description") var description: String, @ColumnInfo(name = "qty") var quantity: Long)

Simple entity

@Entity(tableName = "products")class ProductEntity( @PrimaryKey() @ColumnInfo(name = "id") var id: Long, @ColumnInfo(name = "name") var name: String, @ColumnInfo(name = "description") var description: String, @ColumnInfo(name = "qty") var quantity: Long)

Indexes

@Entity(tableName = "inventory", indices = arrayOf(Index("provider_id", "product_id", unique = true)))class InventoryEntity( @PrimaryKey() var id: Long, @ColumnInfo(name = "provider_id") var providerId: Long, @ColumnInfo(name = "product_id") var productId: Long, @ColumnInfo(name = "qty") var quantity: Long)

Composed PK

@Entity(tableName = "inventory", primaryKeys = arrayOf("provider_id", "product_id"))class InventoryEntity( @ColumnInfo(name = "provider_id") var providerId: Long, @ColumnInfo(name = "product_id") var productId: Long, @ColumnInfo(name = "qty") var quantity: Long)

Foreign keys@Entity( tableName = "inventory", foreignKeys = arrayOf( ForeignKey( entity = ProductEntity::class, parentColumns = arrayOf("id"), childColumns = arrayOf("product_id") ) ))class InventoryEntity( @PrimaryKey() var id: Long, @ColumnInfo(name = "provider_id") var providerId: Long, @ColumnInfo(name = "product_id") var productId: Long, @ColumnInfo(name = "qty") var quantity: Long)

Foreign keys@Entity( tableName = "inventory", foreignKeys = arrayOf( ForeignKey( entity = ProductEntity::class, parentColumns = arrayOf("id"), childColumns = arrayOf("product_id") ) ))class InventoryEntity( @PrimaryKey() var id: Long, @ColumnInfo(name = "provider_id") var providerId: Long, @ColumnInfo(name = "product_id") var productId: Long, @ColumnInfo(name = "qty") var quantity: Long)

Foreign keys@Entity( tableName = "inventory", foreignKeys = arrayOf( ForeignKey( entity = ProductEntity::class, parentColumns = arrayOf("id"), childColumns = arrayOf("product_id") ) ))class InventoryEntity( @PrimaryKey() var id: Long, @ColumnInfo(name = "provider_id") var providerId: Long, @ColumnInfo(name = "product_id") var productId: Long, @ColumnInfo(name = "qty") var quantity: Long)

Foreign keys@Entity( tableName = "inventory", foreignKeys = arrayOf( ForeignKey( entity = ProductEntity::class, parentColumns = arrayOf("id"), childColumns = arrayOf("product_id") ) ))class InventoryEntity( @PrimaryKey() var id: Long, @ColumnInfo(name = "provider_id") var providerId: Long, @ColumnInfo(name = "product_id") var productId: Long, @ColumnInfo(name = "qty") var quantity: Long)

Foreign keys@Entity( tableName = "inventory", foreignKeys = arrayOf( ForeignKey( entity = ProductEntity::class, parentColumns = arrayOf("id"), childColumns = arrayOf("product_id") ) ))class InventoryEntity( @PrimaryKey() var id: Long, @ColumnInfo(name = "provider_id") var providerId: Long, @ColumnInfo(name = "product_id") var productId: Long, @ColumnInfo(name = "qty") var quantity: Long)

Foreign keys@Entity( tableName = "inventory", foreignKeys = arrayOf( ForeignKey( entity = ProductEntity::class, parentColumns = arrayOf("id"), childColumns = arrayOf("product_id") ) ))class InventoryEntity( @PrimaryKey() var id: Long, @ColumnInfo(name = "provider_id") var providerId: Long, @ColumnInfo(name = "product_id") var productId: Long, @ColumnInfo(name = "qty") var quantity: Long)

Nested objects@Entity(tableName = "products")class ProductEntity( @PrimaryKey() @ColumnInfo(name = "id") var id: Long, @ColumnInfo(name = "name") var name: String, @ColumnInfo(name = "description") var description: String, @Embedded var info: ProductInfo)

class ProductInfo( @ColumnInfo(name = "color") var color: String, @ColumnInfo(name = "size") var size: String)

products

id name description color size

Nested objects@Entity(tableName = "products")class ProductEntity( @PrimaryKey() @ColumnInfo(name = "id") var id: Long, @ColumnInfo(name = "name") var name: String, @ColumnInfo(name = "description") var description: String, @Embedded var info: ProductInfo)

class ProductInfo( @ColumnInfo(name = "color") var color: String, @ColumnInfo(name = "size") var size: String)

products

id name description color size

Nested objects@Entity(tableName = "products")class ProductEntity( @PrimaryKey() @ColumnInfo(name = "id") var id: Long, @ColumnInfo(name = "name") var name: String, @ColumnInfo(name = "description") var description: String, @Embedded var info: ProductInfo)

class ProductInfo( @ColumnInfo(name = "color") var color: String, @ColumnInfo(name = "size") var size: String)

products

id name description color size

Nested objects@Entity(tableName = "products")class ProductEntity( @PrimaryKey() @ColumnInfo(name = "id") var id: Long, @ColumnInfo(name = "name") var name: String, @ColumnInfo(name = "description") var description: String, @Embedded var info: ProductInfo)

class ProductInfo( @ColumnInfo(name = "color") var color: String, @ColumnInfo(name = "size") var size: String)

products

id name description color size

DAOs

Simple DAO@Daointerface ProductsDao {

@Insert(onConflict = REPLACE) fun insert(productEntity: ProductEntity): Long

@Query("Select * from products") fun getAll(): List<ProductEntity>

@Update fun update(productEntity: ProductEntity): Int

@Delete fun delete(productEntity: ProductEntity): Int

}

Simple DAO@Daointerface ProductsDao {

@Insert(onConflict = REPLACE) fun insert(productEntity: ProductEntity): Long

@Query("Select * from products") fun getAll(): List<ProductEntity>

@Update fun update(productEntity: ProductEntity): Int

@Delete fun delete(productEntity: ProductEntity): Int

}

Simple DAO@Daointerface ProductsDao {

@Insert(onConflict = REPLACE) fun insert(productEntity: ProductEntity): Long

@Query("Select * from products") fun getAll(): List<ProductEntity>

@Update fun update(productEntity: ProductEntity): Int

@Delete fun delete(productEntity: ProductEntity): Int

}

Row id

Simple DAO@Daointerface ProductsDao {

@Insert(onConflict = REPLACE) fun insert(productEntity: ProductEntity): Long

@Query("Select * from products") fun getAll(): List<ProductEntity>

@Update fun update(productEntity: ProductEntity): Int

@Delete fun delete(productEntity: ProductEntity): Int

}

Simple DAO@Daointerface ProductsDao {

@Insert(onConflict = REPLACE) fun insert(productEntity: ProductEntity): Long

@Query("Select * from products") fun getAll(): List<ProductEntity>

@Update fun update(productEntity: ProductEntity): Int

@Delete fun delete(productEntity: ProductEntity): Int

}

# Modified rows

Simple DAO@Daointerface ProductsDao {

@Insert(onConflict = REPLACE) fun insert(productEntity: ProductEntity): Long

@Query("Select * from products") fun getAll(): List<ProductEntity>

@Update fun update(productEntity: ProductEntity): Int

@Delete fun delete(productEntity: ProductEntity): Int

}

Insert@Daointerface ProductsDao {

@Insert(onConflict = REPLACE) fun insert(product: ProductEntity): Long

@Insert(onConflict = REPLACE) fun insertAll(vararg products: ProductEntity) : List<Long>

@Insert(onConflict = ROLLBACK) fun insertTwo(product: ProductEntity, otherProduct: ProductEntity): List<Long>

@Insert(onConflict = ABORT) fun weirdInsert(product: ProductEntity, products: List<ProductEntity>): List<Long>

}

Update@Daointerface ProductsDao {

@Update() fun update(product: ProductEntity): Int

@Update() fun update(vararg users: ProductEntity): Int

}

Delete@Daointerface ProductsDao {

@Delete() fun delete(product: ProductEntity): Int

@Delete() fun delete(vararg users: ProductEntity): Int

}

Only primary key is matched

Query@Daointerface ProductsDao {

@Query("Select * from products") fun getAllAsList(): List<ProductEntity>

@Query("SELECT * FROM products") fun getAllAsArray(): Array<ProductEntity>

@Query("SELECT * FROM products WHERE name = :name") fun getProductsWithName(name: String): List<ProductEntity>

@Query("SELECT * FROM products WHERE name like :name OR description like :name") fun searchProduct(term: String): List<ProductEntity>

@Query("SELECT * FROM products WHERE name in (:names)") fun getProductsByName(names: List<String>): List<ProductEntity>

@Query("SELECT * FROM products WHERE qty >= :minQty AND qty <= :maxQty") fun getProductsWithinInventory(minQty: Int, maxQty: Int): List<ProductEntity>}

Query@Daointerface ProductsDao {

@Query("Select * from products") fun getAllAsList(): List<ProductEntity>

@Query("SELECT * FROM products") fun getAllAsArray(): Array<ProductEntity>

@Query("SELECT * FROM products WHERE name = :name") fun getProductsWithName(name: String): List<ProductEntity>

@Query("SELECT * FROM products WHERE name like :name OR description like :name") fun searchProduct(term: String): List<ProductEntity>

@Query("SELECT * FROM products WHERE name in (:names)") fun getProductsByName(names: List<String>): List<ProductEntity>

@Query("SELECT * FROM products WHERE qty >= :minQty AND qty <= :maxQty") fun getProductsWithinInventory(minQty: Int, maxQty: Int): List<ProductEntity>}

Query@Daointerface ProductsDao {

@Query("Select * from products") fun getAllAsList(): List<ProductEntity>

@Query("SELECT * FROM products") fun getAllAsArray(): Array<ProductEntity>

@Query("SELECT * FROM products WHERE name = :name") fun getProductsWithName(name: String): List<ProductEntity>

@Query("SELECT * FROM products WHERE name like :name OR description like :name") fun searchProduct(term: String): List<ProductEntity>

@Query("SELECT * FROM products WHERE name in (:names)") fun getProductsByName(names: List<String>): List<ProductEntity>

@Query("SELECT * FROM products WHERE qty >= :minQty AND qty <= :maxQty") fun getProductsWithinInventory(minQty: Int, maxQty: Int): List<ProductEntity>}

Query@Daointerface ProductsDao {

@Query("Select * from products") fun getAllAsList(): List<ProductEntity>

@Query("SELECT * FROM products") fun getAllAsArray(): Array<ProductEntity>

@Query("SELECT * FROM products WHERE name = :name") fun getProductsWithName(name: String): List<ProductEntity>

@Query("SELECT * FROM products WHERE name like :name OR description like :name") fun searchProduct(term: String): List<ProductEntity>

@Query("SELECT * FROM products WHERE name in (:names)") fun getProductsByName(names: List<String>): List<ProductEntity>

@Query("SELECT * FROM products WHERE qty >= :minQty AND qty <= :maxQty") fun getProductsWithinInventory(minQty: Int, maxQty: Int): List<ProductEntity>}

Query@Daointerface ProductsDao {

@Query("Select * from products") fun getAllAsList(): List<ProductEntity>

@Query("SELECT * FROM products") fun getAllAsArray(): Array<ProductEntity>

@Query("SELECT * FROM products WHERE name = :name") fun getProductsWithName(name: String): List<ProductEntity>

@Query("SELECT * FROM products WHERE name like :name OR description like :name") fun searchProduct(term: String): List<ProductEntity>

@Query("SELECT * FROM products WHERE name in (:names)") fun getProductsByName(names: List<String>): List<ProductEntity>

@Query("SELECT * FROM products WHERE qty >= :minQty AND qty <= :maxQty") fun getProductsWithinInventory(minQty: Int, maxQty: Int): List<ProductEntity>}

Query@Daointerface ProductsDao {

@Query("Select * from products") fun getAllAsList(): List<ProductEntity>

@Query("SELECT * FROM products") fun getAllAsArray(): Array<ProductEntity>

@Query("SELECT * FROM products WHERE name = :name") fun getProductsWithName(name: String): List<ProductEntity>

@Query("SELECT * FROM products WHERE name like :name OR description like :name") fun searchProduct(term: String): List<ProductEntity>

@Query("SELECT * FROM products WHERE name in (:names)") fun getProductsByName(names: List<String>): List<ProductEntity>

@Query("SELECT * FROM products WHERE qty >= :minQty AND qty <= :maxQty") fun getProductsWithinInventory(minQty: Int, maxQty: Int): List<ProductEntity>}

Query@Daointerface ProductsDao {

@Query("Select * from products") fun getAllAsList(): List<ProductEntity>

@Query("SELECT * FROM products") fun getAllAsArray(): Array<ProductEntity>

@Query("SELECT * FROM products WHERE name = :name") fun getProductsWithName(name: String): List<ProductEntity>

@Query("SELECT * FROM products WHERE name like :name OR description like :name") fun searchProduct(term: String): List<ProductEntity>

@Query("SELECT * FROM products WHERE name in (:names)") fun getProductsByName(names: List<String>): List<ProductEntity>

@Query("SELECT * FROM products WHERE qty >= :minQty AND qty <= :maxQty") fun getProductsWithinInventory(minQty: Int, maxQty: Int): List<ProductEntity>}

Query@Daointerface ProductsDao { @Query("SELECT * FROM products WHERE id = :id") fun getProducts(id: Long): ProductEntity

@Query("SELECT COUNT(*) FROM products") fun getProductsCount(): Int

@Query("SELECT COUNT(*) AS count, name FROM products GROUP BY name") fun getProductsCountByName(): List<ProductCountEntity>}

class ProductCountEntity( @ColumnInfo(name = "name") var name: String, @ColumnInfo(name = "count") var count: Int)

Query@Daointerface ProductsDao { @Query("SELECT * FROM products WHERE id = :id") fun getProducts(id: Long): ProductEntity

@Query("SELECT COUNT(*) FROM products") fun getProductsCount(): Int

@Query("SELECT COUNT(*) AS count, name FROM products GROUP BY name") fun getProductsCountByName(): List<ProductCountEntity>}

class ProductCountEntity( @ColumnInfo(name = "name") var name: String, @ColumnInfo(name = "count") var count: Int)

Query@Daointerface ProductsDao { @Query("SELECT * FROM products WHERE id = :id") fun getProducts(id: Long): ProductEntity

@Query("SELECT COUNT(*) FROM products") fun getProductsCount(): Int

@Query("SELECT COUNT(*) AS count, name FROM products GROUP BY name") fun getProductsCountByName(): List<ProductCountEntity>}

class ProductCountEntity( @ColumnInfo(name = "name") var name: String, @ColumnInfo(name = "count") var count: Int)

Query@Daointerface ProductsDao { @Query("SELECT * FROM products WHERE id = :id") fun getProducts(id: Long): ProductEntity

@Query("SELECT COUNT(*) FROM products") fun getProductsCount(): Int

@Query("SELECT COUNT(*) AS count, name FROM products GROUP BY name") fun getProductsCountByName(): List<ProductCountEntity>}

class ProductCountEntity( @ColumnInfo(name = "name") var name: String, @ColumnInfo(name = "count") var count: Int)

Database

Database@Database(

entities = arrayOf(

ProductEntity::class,

InventoryEntity::class

),

version = 1

)

abstract class MyDatabase : RoomDatabase() {

abstract fun productsDao(): ProductsDao

abstract fun inventoryDao(): InventoryDao

}

Database@Database(

entities = arrayOf(

ProductEntity::class,

InventoryEntity::class

),

version = 1

)

abstract class MyDatabase : RoomDatabase() {

abstract fun productsDao(): ProductsDao

abstract fun inventoryDao(): InventoryDao

}

Database@Database(

entities = arrayOf(

ProductEntity::class,

InventoryEntity::class

),

version = 1

)

abstract class MyDatabase : RoomDatabase() {

abstract fun productsDao(): ProductsDao

abstract fun inventoryDao(): InventoryDao

}

Database@Database(

entities = arrayOf(

ProductEntity::class,

InventoryEntity::class

),

version = 1

)

abstract class MyDatabase : RoomDatabase() {

abstract fun productsDao(): ProductsDao

abstract fun inventoryDao(): InventoryDao

}

Using databaseval database: MyDatabase = Room

.databaseBuilder(applicationContext, MyDatabase::class.java, "database")

.build()

val products = database.productsDao().getAllAsList()

Using databaseval database: MyDatabase = Room

.databaseBuilder(applicationContext, MyDatabase::class.java, "database")

.build()

val products = database.productsDao().getAllAsList()

Using databaseval database: MyDatabase = Room

.databaseBuilder(applicationContext, MyDatabase::class.java, "database")

.build()

val products = database.productsDao().getAllAsList()

Use a single instance of database!

Type Converters

Date (Boo)vs.

Three-ten-abp (Yaay!)

Type convertersclass DateTimeConverter {

private val df : DateTimeFormatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME

@TypeConverter

fun toTimestamp(dateTime: LocalDateTime) : String {

return dateTime.format(df)

}

@TypeConverter

fun toDateTime(timestamp:String) : LocalDateTime {

return LocalDateTime.parse(timestamp, df);

}

}

Type convertersclass DateTimeConverter {

private val df : DateTimeFormatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME

@TypeConverter

fun toTimestamp(dateTime: LocalDateTime) : String {

return dateTime.format(df)

}

@TypeConverter

fun toDateTime(timestamp:String) : LocalDateTime {

return LocalDateTime.parse(timestamp, df);

}

}

Type converters@Database(entities = arrayOf(ProductEntity::class), version = 1)

@TypeConverters(DateTimeConverter::class)

abstract class MyDatabase : RoomDatabase() {

abstract fun productsDao(): ProductsDao

}

Type converters@Dao

@TypeConverters(DateTimeConverter::class)

interface InventoryDao {

@Query("Select * from inventory where date = :date")

fun getByDate(date: LocalDateTime)

@Query("Select * from inventory where date = :date")

fun getByDate2(date: LocalDateTime)

}

Type converters@Dao

interface InventoryDao {

@Query("Select * from inventory where date = :date")

@TypeConverters(DateTimeConverter::class)

fun getByDate(date: LocalDateTime)

@Query("Select * from inventory where date = :date")

fun getByDate2(date: LocalDateTime)

}

Type converters@Dao

interface InventoryDao {

@Query("Select * from inventory where date = :date")

fun getByDate(date: LocalDateTime)

@Query("Select * from inventory where date = :date")

fun getByDate2(@TypeConverters(DateTimeConverter::class) date: LocalDateTime)

}

Type converters@Entity(tableName = "products")

class ProductEntity(

@PrimaryKey() @ColumnInfo(name = "id") var id: Long,

@ColumnInfo(name = "name") var name: String,

@ColumnInfo(name = "description") var description: String,

@ColumnInfo(name = "qty") var quantity: Long,

@ColumnInfo(name = "purchase_date") var purchaseDate: LocalDateTime

)

Type converters@Entity(tableName = "products")

@TypeConverters(DateTimeConverter::class)

class ProductEntity(

@PrimaryKey() @ColumnInfo(name = "id") var id: Long,

@ColumnInfo(name = "name") var name: String,

@ColumnInfo(name = "description") var description: String,

@ColumnInfo(name = "qty") var quantity: Long,

@ColumnInfo(name = "purchase_date") var purchaseDate: LocalDateTime

)

Type converters@Entity(tableName = "products")

class ProductEntity(

@PrimaryKey() @ColumnInfo(name = "id") var id: Long,

@ColumnInfo(name = "name") var name: String,

@ColumnInfo(name = "description") var description: String,

@ColumnInfo(name = "qty") var quantity: Long,

@TypeConverters(DateTimeConverter::class)

@ColumnInfo(name = "purchase_date") var purchaseDate: LocalDateTime

)

Migrationsval migrationFrom1To2 = object : Migration(1, 2) {

override fun migrate(database: SupportSQLiteDatabase) {

database.execSQL("ALTER TABLE products ADD COLUMN prices INTEGER")

}

}

val migrationFrom2To3 = object : Migration(2, 3) {

override fun migrate(database: SupportSQLiteDatabase) {

database.execSQL("ALTER TABLE products ADD COLUMN color TEXT")

}

}

val database: MyDatabase = Room

.databaseBuilder(applicationContext, MyDatabase::class.java, "database")

.addMigrations(migrationFrom1To2, migrationFrom2To3)

.build()

Migrationsval migrationFrom1To2 = object : Migration(1, 2) {

override fun migrate(database: SupportSQLiteDatabase) {

database.execSQL("ALTER TABLE products ADD COLUMN prices INTEGER")

}

}

val migrationFrom2To3 = object : Migration(2, 3) {

override fun migrate(database: SupportSQLiteDatabase) {

database.execSQL("ALTER TABLE products ADD COLUMN color TEXT")

}

}

val database: MyDatabase = Room

.databaseBuilder(applicationContext, MyDatabase::class.java, "database")

.addMigrations(migrationFrom1To2, migrationFrom2To3)

.build()

Migrationsval migrationFrom1To2 = object : Migration(1, 2) {

override fun migrate(database: SupportSQLiteDatabase) {

database.execSQL("ALTER TABLE products ADD COLUMN prices INTEGER")

}

}

val migrationFrom2To3 = object : Migration(2, 3) {

override fun migrate(database: SupportSQLiteDatabase) {

database.execSQL("ALTER TABLE products ADD COLUMN color TEXT")

}

}

val database: MyDatabase = Room

.databaseBuilder(applicationContext, MyDatabase::class.java, "database")

.addMigrations(migrationFrom1To2, migrationFrom2To3)

.build()

Migrationsval migrationFrom1To2 = object : Migration(1, 2) {

override fun migrate(database: SupportSQLiteDatabase) {

database.execSQL("ALTER TABLE products ADD COLUMN prices INTEGER")

}

}

val migrationFrom2To3 = object : Migration(2, 3) {

override fun migrate(database: SupportSQLiteDatabase) {

database.execSQL("ALTER TABLE products ADD COLUMN color TEXT")

}

}

val database: MyDatabase = Room

.databaseBuilder(applicationContext, MyDatabase::class.java, "database")

.addMigrations(migrationFrom1To2, migrationFrom2To3)

.build()

Migrations@Database(entities = arrayOf(ProductEntity::class), version = 3)

@TypeConverters(DateTimeConverter::class)

abstract class MyDatabase : RoomDatabase() {

abstract fun productsDao(): ProductsDao

}

Warning!

If no migration is provided, Room will rebuilt database

based on schema

LiveData

@Query("Select * from products")

fun getAllAsList(): LiveData<List<ProductEntity>>

RxJava 2

def room = '1.0.0-beta2'

implementation "android.arch.persistence.room:runtime:${room}"

kapt "android.arch.persistence.room:compiler:${room}"

implementation "android.arch.persistence.room:rxjava2:${room}"

RxJava 2@Query("Select * from products")

fun getAllAsList(): Flowable<List<ProductEntity>>

@Query("SELECT * FROM products WHERE id = :id")

fun getProductById(id: Long): Single<ProductEntity>

@Query("SELECT * FROM products WHERE name = :name")

fun getProductByName(name: String): Maybe<ProductEntity>

RxJava 2

Flowable Maybe Single

No data - Just onComplete onError

Data onNext onSuccess onSuccess

Update onNext - -

Moar things!

But enough for today

Questions?