# Quickstart
This page walks through a complete query end-to-end.
## The full example
```java
import org.apache.arrow.memory.RootAllocator;
import org.apache.arrow.vector.ipc.ArrowReader;
import org.apache.datafusion.DataFrame;
import org.apache.datafusion.SessionContext;
try (var allocator = new RootAllocator();
var ctx = new SessionContext()) {
ctx.registerParquet("orders", "/path/to/orders.parquet");
try (DataFrame df = ctx.sql(
"SELECT o_orderpriority, COUNT(*) AS n " +
"FROM orders GROUP BY o_orderpriority");
ArrowReader reader = df.collect(allocator)) {
while (reader.loadNextBatch()) {
var batch = reader.getVectorSchemaRoot();
// ...
}
}
}
```
## Walkthrough
**Allocator.** `RootAllocator` is the Arrow off-heap memory allocator. Every
JVM-side Arrow buffer is tracked under an allocator; when the allocator is
closed, leaked buffers are reported. Use one allocator per query (or one
per application) and close it in a `try`-with-resources.
**Session context.** `SessionContext` is the entry point into DataFusion. It
holds the catalog of registered tables and the query planner. It is
`AutoCloseable` and **not thread-safe** use one per thread, or guard
access externally.
**Registering data.** `registerParquet(name, path)` reads the file's footer
on call and exposes it under the given table name. See
[Parquet](parquet.md) for the options form.
**SQL.** `ctx.sql("...")` plans the query and returns a `DataFrame`. The
query is not executed until results are pulled.
**Collecting results.** `df.collect(allocator)` starts native execution and
returns an `ArrowReader`. Each `loadNextBatch()` call pulls the next
`VectorSchemaRoot`; iterate until it returns `false`.
**Cleanup.** Both `SessionContext` and `DataFrame` are `AutoCloseable`. Use
`try`-with-resources so native resources and Arrow buffers are released
even on exception.