diff --git a/sqlite/Makefile b/sqlite/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..527a9d7c887a2e34820b90f29bdf979ee01bd42d
--- /dev/null
+++ b/sqlite/Makefile
@@ -0,0 +1,12 @@
+build:
+	go build && go install
+
+reset-db:
+	$(MAKE) -C _dumps
+
+test: reset-db
+	go test -v
+	$(MAKE) -C _example
+
+bench: reset-db
+	go test -v -test.bench=. -test.benchtime=10s -benchmem
diff --git a/sqlite/_dumps/Makefile b/sqlite/_dumps/Makefile
index 400fdf203d56696cd7a19e1b618944aaa7e366bb..8102fbfdc684bbf3656a75d75528b5d916bd590c 100644
--- a/sqlite/_dumps/Makefile
+++ b/sqlite/_dumps/Makefile
@@ -1,5 +1,5 @@
-all:
-	rm -f gotest.sqlite3.db
+load: clean
 	cat structs.sql | sqlite3 gotest.sqlite3.db
+
 clean:
 	rm -f gotest.sqlite3.db
diff --git a/sqlite/_example/Makefile b/sqlite/_example/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..04d4100ddd2a31ae26a67e97e4768cbb01699edf
--- /dev/null
+++ b/sqlite/_example/Makefile
@@ -0,0 +1,4 @@
+test:
+	rm -f example.db
+	cat example.sql | sqlite3 example.db
+	go run -v main.go
diff --git a/sqlite/collection.go b/sqlite/collection.go
index 0aadfcd010ce8ae64d1e4ce1bc6321b3d69bd9c4..676f8fef51dc6e54202d4120217324a218be72cb 100644
--- a/sqlite/collection.go
+++ b/sqlite/collection.go
@@ -47,7 +47,7 @@ func (t *table) Find(terms ...interface{}) db.Result {
 
 // Truncate deletes all rows from the table.
 func (t *table) Truncate() error {
-	_, err := t.database.Exec(sqlgen.Statement{
+	_, err := t.database.Exec(&sqlgen.Statement{
 		Type:  sqlgen.Truncate,
 		Table: sqlgen.TableWithName(t.MainTableName()),
 	})
@@ -81,7 +81,7 @@ func (t *table) Append(item interface{}) (interface{}, error) {
 		}
 	}
 
-	stmt := sqlgen.Statement{
+	stmt := &sqlgen.Statement{
 		Type:    sqlgen.Insert,
 		Table:   sqlgen.TableWithName(t.MainTableName()),
 		Columns: sqlgenCols,
diff --git a/sqlite/database.go b/sqlite/database.go
index 22de80bb25b0fbb068dfe720649e18137d903a95..ecfa0d60232044662fc883e699c4cace1e73dce5 100644
--- a/sqlite/database.go
+++ b/sqlite/database.go
@@ -29,6 +29,7 @@ import (
 
 	"github.com/jmoiron/sqlx"
 	_ "github.com/mattn/go-sqlite3" // SQLite3 driver.
+	"upper.io/cache"
 	"upper.io/db"
 	"upper.io/db/util/schema"
 	"upper.io/db/util/sqlgen"
@@ -48,7 +49,8 @@ type database struct {
 	// columns property was introduced so we could query PRAGMA data only once
 	// and retrieve all the column information we'd need, such as name and if it
 	// is a primary key.
-	columns map[string][]columnSchemaT
+	columns          map[string][]columnSchemaT
+	cachedStatements *cache.Cache
 }
 
 type tx struct {
@@ -56,6 +58,11 @@ type tx struct {
 	*database
 }
 
+type cachedStatement struct {
+	*sqlx.Stmt
+	query string
+}
+
 var (
 	_ = db.Database(&database{})
 	_ = db.Tx(&tx{})
@@ -66,6 +73,40 @@ type columnSchemaT struct {
 	PK   int    `db:"pk"`
 }
 
+func (d *database) prepareStatement(stmt *sqlgen.Statement) (p *sqlx.Stmt, query string, err error) {
+	if d.session == nil {
+		return nil, "", db.ErrNotConnected
+	}
+
+	pc, ok := d.cachedStatements.ReadRaw(stmt)
+
+	if ok {
+		ps := pc.(*cachedStatement)
+		p = ps.Stmt
+		query = ps.query
+	} else {
+		query = compileAndReplacePlaceholders(stmt)
+
+		if d.tx != nil {
+			p, err = d.tx.Preparex(query)
+		} else {
+			p, err = d.session.Preparex(query)
+		}
+
+		if err != nil {
+			return nil, "", err
+		}
+
+		d.cachedStatements.Write(stmt, &cachedStatement{p, query})
+	}
+
+	return p, query, nil
+}
+
+func compileAndReplacePlaceholders(stmt *sqlgen.Statement) string {
+	return stmt.Compile(template.Template)
+}
+
 // Driver returns the underlying *sqlx.DB instance.
 func (d *database) Driver() interface{} {
 	return d.session
@@ -96,8 +137,12 @@ func (d *database) Open() error {
 
 	d.session.Mapper = sqlutil.NewMapper()
 
-	if err = d.populateSchema(); err != nil {
-		return err
+	d.cachedStatements = cache.NewCache()
+
+	if d.schema == nil {
+		if err = d.populateSchema(); err != nil {
+			return err
+		}
 	}
 
 	return nil
@@ -110,14 +155,13 @@ func (d *database) Clone() (db.Database, error) {
 }
 
 func (d *database) clone() (*database, error) {
-	src := &database{}
-	src.Setup(d.connURL)
-
-	if err := src.Open(); err != nil {
+	clone := &database{
+		schema: d.schema,
+	}
+	if err := clone.Setup(d.connURL); err != nil {
 		return nil, err
 	}
-
-	return src, nil
+	return clone, nil
 }
 
 // Ping checks whether a connection to the database is still alive by pinging
@@ -187,7 +231,7 @@ func (d *database) Collections() (collections []string, err error) {
 	// Schema is empty.
 
 	// Querying table names.
-	stmt := sqlgen.Statement{
+	stmt := &sqlgen.Statement{
 		Type: sqlgen.Select,
 		Columns: sqlgen.JoinColumns(
 			sqlgen.ColumnWithName(`tbl_name`),
@@ -231,24 +275,26 @@ func (d *database) Collections() (collections []string, err error) {
 }
 
 // Use changes the active database.
-func (d *database) Use(database string) (err error) {
+func (d *database) Use(name string) (err error) {
 	var conn ConnectionURL
 
 	if conn, err = ParseURL(d.connURL.String()); err != nil {
 		return err
 	}
 
-	conn.Database = database
+	conn.Database = name
 
 	d.connURL = conn
 
+	d.schema = nil
+
 	return d.Open()
 }
 
 // Drop removes all tables from the current database.
 func (d *database) Drop() error {
 
-	_, err := d.Query(sqlgen.Statement{
+	_, err := d.Query(&sqlgen.Statement{
 		Type:     sqlgen.DropDatabase,
 		Database: sqlgen.DatabaseWithName(d.schema.Name),
 	})
@@ -288,90 +334,72 @@ func (d *database) Transaction() (db.Tx, error) {
 }
 
 // Exec compiles and executes a statement that does not return any rows.
-func (d *database) Exec(stmt sqlgen.Statement, args ...interface{}) (sql.Result, error) {
+func (d *database) Exec(stmt *sqlgen.Statement, args ...interface{}) (sql.Result, error) {
 	var query string
-	var res sql.Result
+	var p *sqlx.Stmt
 	var err error
-	var start, end int64
 
-	start = time.Now().UnixNano()
-
-	defer func() {
-		end = time.Now().UnixNano()
-		sqlutil.Log(query, args, err, start, end)
-	}()
+	if db.Debug {
+		var start, end int64
+		start = time.Now().UnixNano()
 
-	if d.session == nil {
-		return nil, db.ErrNotConnected
+		defer func() {
+			end = time.Now().UnixNano()
+			sqlutil.Log(query, args, err, start, end)
+		}()
 	}
 
-	query = stmt.Compile(template.Template)
-
-	if d.tx != nil {
-		res, err = d.tx.Exec(query, args...)
-	} else {
-		res, err = d.session.Exec(query, args...)
+	if p, query, err = d.prepareStatement(stmt); err != nil {
+		return nil, err
 	}
 
-	return res, err
+	return p.Exec(args...)
 }
 
 // Query compiles and executes a statement that returns rows.
-func (d *database) Query(stmt sqlgen.Statement, args ...interface{}) (*sqlx.Rows, error) {
-	var rows *sqlx.Rows
+func (d *database) Query(stmt *sqlgen.Statement, args ...interface{}) (*sqlx.Rows, error) {
 	var query string
+	var p *sqlx.Stmt
 	var err error
-	var start, end int64
 
-	start = time.Now().UnixNano()
+	if db.Debug {
+		var start, end int64
+		start = time.Now().UnixNano()
 
-	defer func() {
-		end = time.Now().UnixNano()
-		sqlutil.Log(query, args, err, start, end)
-	}()
-
-	if d.session == nil {
-		return nil, db.ErrNotConnected
+		defer func() {
+			end = time.Now().UnixNano()
+			sqlutil.Log(query, args, err, start, end)
+		}()
 	}
 
-	query = stmt.Compile(template.Template)
-
-	if d.tx != nil {
-		rows, err = d.tx.Queryx(query, args...)
-	} else {
-		rows, err = d.session.Queryx(query, args...)
+	if p, query, err = d.prepareStatement(stmt); err != nil {
+		return nil, err
 	}
 
-	return rows, err
+	return p.Queryx(args...)
 }
 
 // QueryRow compiles and executes a statement that returns at most one row.
-func (d *database) QueryRow(stmt sqlgen.Statement, args ...interface{}) (*sqlx.Row, error) {
+func (d *database) QueryRow(stmt *sqlgen.Statement, args ...interface{}) (*sqlx.Row, error) {
 	var query string
-	var row *sqlx.Row
+	var p *sqlx.Stmt
 	var err error
-	var start, end int64
 
-	start = time.Now().UnixNano()
+	if db.Debug {
+		var start, end int64
+		start = time.Now().UnixNano()
 
-	defer func() {
-		end = time.Now().UnixNano()
-		sqlutil.Log(query, args, err, start, end)
-	}()
-
-	if d.session == nil {
-		return nil, db.ErrNotConnected
+		defer func() {
+			end = time.Now().UnixNano()
+			sqlutil.Log(query, args, err, start, end)
+		}()
 	}
 
-	query = stmt.Compile(template.Template)
-
-	if d.tx != nil {
-		row = d.tx.QueryRowx(query, args...)
-	} else {
-		row = d.session.QueryRowx(query, args...)
+	if p, query, err = d.prepareStatement(stmt); err != nil {
+		return nil, err
 	}
 
-	return row, err
+	return p.QueryRowx(args...), nil
 }
 
 // populateSchema looks up for the table info in the database and populates its
@@ -405,7 +433,7 @@ func (d *database) populateSchema() (err error) {
 }
 
 func (d *database) tableExists(names ...string) error {
-	var stmt sqlgen.Statement
+	var stmt *sqlgen.Statement
 	var err error
 	var rows *sqlx.Rows
 
@@ -416,7 +444,7 @@ func (d *database) tableExists(names ...string) error {
 			continue
 		}
 
-		stmt = sqlgen.Statement{
+		stmt = &sqlgen.Statement{
 			Type:  sqlgen.Select,
 			Table: sqlgen.TableWithName(`sqlite_master`),
 			Columns: sqlgen.JoinColumns(
diff --git a/sqlite/database_test.go b/sqlite/database_test.go
index f52f6a84f8360392e6c69c8bb5a6a112c6c837a0..bae89a2e491541160755cf81a95c487d1490c03a 100644
--- a/sqlite/database_test.go
+++ b/sqlite/database_test.go
@@ -1421,7 +1421,7 @@ func BenchmarkAppendRawSQLWithArgs(b *testing.B) {
 		b.Fatal(err)
 	}
 
-	args = []interface{}{
+	args := []interface{}{
 		"Hayao Miyazaki",
 	}