{"id":1123,"date":"2010-07-28T12:07:30","date_gmt":"2010-07-28T17:07:30","guid":{"rendered":"http:\/\/www.andygibson.net\/blog\/?p=1123"},"modified":"2010-07-28T12:07:52","modified_gmt":"2010-07-28T17:07:52","slug":"accessing-and-paginating-csv-files-with-datavalve","status":"publish","type":"post","link":"http:\/\/www.andygibson.net\/blog\/tutorial\/accessing-and-paginating-csv-files-with-datavalve\/","title":{"rendered":"Accessing and Paginating CSV Files with DataValve"},"content":{"rendered":"<div class=\"prereq floatRight\">\n<ul> <lh>Prerequisites<\/lh><\/p>\n<li>Install Maven<\/li>\n<li><a href=\"http:\/\/www.andygibson.net\/blog\/tutorial\/install-datavalve-into-maven\/\">Installing DataValve Into Maven<\/a><\/li>\n<\/ul>\n<\/div>\n<p>While <a href=\"http:\/\/www.andygibson.net\/blog\/projects\/datavalve\/\">DataValve<\/a> is mostly used with database driven back ends, this tutorial shows you how DataValve can turn a comma delimited file into a paginated list of objects that the user can page through. We will then use this data provider in a console application, a Swing application and a JSF web page using the DataValve data client interfaces.<br \/>\n <!--more--><br \/>\nWe will start by writing this tutorial as a console application and then demonstrate how the data provider can be used in other applications, even web applications. We&#8217;ll start by creating a new Maven application and then include the dependencies for DataValve. We&#8217;ll then create our comma delimited data provider, define a row mapper class for it, and hook it up to our demo data. We&#8217;ll then attach it to a dataset so we can easily iterate through the data. We will then take our provider and use it in different clients.<\/p>\n<ol>\n<li>Create a new Maven application in your IDE and add <code>datavalve-dataset<\/code>  as a dependency in your <code>pom.xml<\/code> file.\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n\t&lt;dependencies&gt;\r\n\t\t&lt;dependency&gt;\r\n\t\t\t&lt;groupId&gt;org.fluttercode.datavalve&lt;\/groupId&gt;\r\n\t\t\t&lt;artifactId&gt;datavalve-dataset&lt;\/artifactId&gt;\r\n\t\t\t&lt;version&gt;0.9.0.CR2&lt;\/version&gt;\r\n\t\t&lt;\/dependency&gt;\r\n\t&lt;\/dependencies&gt;\r\n<\/pre>\n<\/li>\n<li>Create a new class called <code>Person<\/code> in the package of your choice. I used <code>org.fluttercode.tutorials.datavalve.csvreader<\/code> We will only be using one package in this demonstration.<\/li>\n<li>The <code>Person<\/code> class will be our model which will be populated from the comma delimited file.\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npublic class Person {\r\n\r\n\tprivate Integer id;\r\n\tprivate String firstName;\r\n\tprivate String middleName;\r\n\tprivate String lastName;\r\n\tprivate Date dob;\r\n\tprivate String email;\r\n\tprivate String address;\r\n\tprivate String city;\r\n\tprivate String zip;\r\n\r\n\tpublic Person() {\r\n\t}\r\n\r\n\tpublic Person(Integer id, String firstName, String lastName,String middleName,\r\n\t\t\tDate dob, String email, String address,\r\n\t\t\tString city, String zip) {\r\n\t\tsuper();\r\n\t\tthis.id = id;\r\n\t\tthis.firstName = firstName;\r\n\t\tthis.middleName = middleName;\r\n\t\tthis.lastName = lastName;\r\n\t\tthis.email = email;\r\n\t\tthis.dob = dob;\r\n\t\tthis.address = address;\r\n\t\tthis.city = city;\r\n\t\tthis.zip = zip;\r\n\t}\r\n\r\n\tpublic String getName() {\r\n\t\treturn firstName + &quot; &quot; + lastName;\r\n\t}\r\n\r\n\tpublic String getAddressText() {\r\n\t\treturn address+ &quot;,&quot; + city+&quot;,&quot;+zip;\r\n\t}\r\n\r\n\t@Override\r\n\tpublic String toString() {\r\n\t\treturn String.format(&quot;Person [id=%d, name=%s, dob=%s,address=%s, email=%s&quot;,\r\n\t\t    id,getName(),dob,getAddressText(),email);\r\n\t}\r\n\r\n    .... Getters and Setters Omitted ....\r\n}\r\n<\/pre>\n<\/li>\n<li>In order to convert the csv data to an object, we need to create a <code>ColumnarRowMapper<\/code> instance. This takes a row of data, already converted to an array of string values, builds the model object from it and returns it back to the caller. Create a new class called <code>PersonRowMapper<\/code>.<\/li>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npublic class PersonRowMapper implements ColumnarRowMapper&lt;Person&gt; {\r\n\r\n    private static SimpleDateFormat converter = new SimpleDateFormat(\r\n\t\t\t&quot;MM\/dd\/yyyy&quot;);\r\n\r\n\tpublic Person mapRow(String[] values) {\r\n\t\tDate dob = null;\r\n\r\n\t\ttry {\r\n\t\t\tdob = converter.parse(values[5]);\r\n\t\t} catch (ParseException e) {\r\n\t\t}\r\n\r\n\t\treturn new Person(DataConverter.getInteger(values[0]), values[1],\r\n\t\t\t\tvalues[2], values[3], dob, values[4], values[6], values[7],\r\n\t\t\t\tvalues[8]);\r\n\t}\r\n}\r\n<\/pre>\n<p>This row mapper implements the <code>ColumnarRowMapper<\/code> interface and implements the single method to return a built entity object from the array of column values passed in.<\/p>\n<li>The only other element we need is some test data to run it on which you can download from <a href='http:\/\/www.andygibson.net\/blog\/wp-content\/uploads\/2010\/07\/data.zip'>here<\/a>. Download this file and place it in the package folder with your source code. Depending on your IDE, you may need to refresh the folder in the package explorer so it can pick up the new file in the folder. The example file has 100 rows of data in it.<\/li>\n<li>Create a new class called <code>ProviderFactory<\/code> which will create our provider and initialize it with the CSV file url.\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npublic class ProviderFactory {\r\n\t\r\n\tpublic static CommaDelimitedProvider&lt;Person&gt; createProvider() {\r\n\t\tURL url = ProviderFactory.class.getResource(&quot;data.csv&quot;);\r\n\t\tCommaDelimitedProvider&lt;Person&gt; provider;\r\n\t\tprovider = new CommaDelimitedProvider&lt;Person&gt;(url.getFile());\r\n\t\tprovider.setRowMapper(new PersonRowMapper());\r\n\t\treturn provider;\r\n\t}\r\n}\r\n<\/pre>\n<p>We use a <code>URL<\/code> to reference the csv file stored in the jar from which we get a <code>File<\/code> instance that we can pass to our <code>CommaDelimitedProvider<\/code>. This relies on the <code>data.csv<\/code> file being in the same location as this class. Alternatively, you can put the file elsewhere and just create a regular <code>File<\/code> object to it rather than use the <code>URL<\/code>.\n<\/li>\n<li>For the console part of this demo, create a <code>Main<\/code> class and add a <code>main<\/code> method containing the code get an instance of the provider, and perform a simple iteration through the data.\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npublic class Main {\r\n\r\n\tpublic static void main(String[] args) {\r\n\t\tCommaDelimitedProvider&lt;Person&gt; provider;\r\n\t\t\r\n\t\tprovider= ProviderFactory.createProvider();\r\n\r\n\t\tList&lt;Person&gt; results = provider.fetchResults(new DefaultPaginator());\r\n\t\tfor (Person p : results) {\r\n\t\t\tSystem.out.println(p);\r\n\t\t}\r\n\t}\r\n}\r\n<\/pre>\n<p>In this example we are reading all the results at once in memory and displaying the whole list.<\/li>\n<\/ol>\n<h2>Paginating the results<\/h2>\n<p>We can control which results and how many are displayed by using a paginator to control the flow of data. This can be useful if you have a huge file and it would be impractical to read all the results into memory in one go. Datasets have paginators built in and use a reference to a provider to fetch the actual data.<\/p>\n<ol>\n<li>In the <code>main<\/code> method, instead of using the provider directly, create a dataset and pass it a reference to our data provider.\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npublic static void main(String[] args) {\r\n\tCommaDelimitedProvider&lt;Person&gt; provider;\r\n\r\n\tprovider = ProviderFactory.createProvider();\r\n\r\n\tCommaDelimitedDataset&lt;Person&gt; ds = new CommaDelimitedDataset&lt;Person&gt;(provider);\r\n\tds.setMaxRows(10);\r\n\tList&lt;Person&gt; results = ds.getResultList();\r\n\tfor (Person p : results) {\r\n\t\tSystem.out.println(p);\r\n\t}\r\n}\r\n<\/pre>\n<p>We have set the maximum number of rows to 10 so the results returned only contains 10 rows of data at most. We can use this mechanism to control the number of results fetched if they are paginated.<\/li>\n<li>The dataset classes implement the <code>Iterable<\/code> interface which lets us iterate over the entire set of data. By setting the page size, we can control the batch sizes when the data is fetched in while it is being iterated over. This allows us to control the flow of data, either to reduce the in-memory realization of the source data, or to reduce latency in an otherwise slow data source as the application can process one page of data while the provider is loading the next page of data.\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npublic static void main(String[] args)  {\r\n\tCommaDelimitedProvider&lt;Person&gt; provider;\r\n\tprovider = ProviderFactory.createProvider();\r\n\r\n\tCommaDelimitedDataset&lt;Person&gt; ds = new CommaDelimitedDataset&lt;Person&gt;(provider);\r\n\tds.setMaxRows(5);\r\n\tfor (Person p : ds) {\r\n\t\tSystem.out.println(p);\r\n\t}\r\n}\r\n<\/pre>\n<\/li>\n<li>You can see this process in action by extending the comma delimited data provider as an anonymous class and adding logging to the post fetch methods. We do this by modifying the <code>ProviderFactory.createProvider()<\/code> method :\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npublic static CommaDelimitedProvider&lt;Person&gt; createProvider() {\r\n\tURL url = Main.class.getResource(&quot;data.csv&quot;);\r\n\tCommaDelimitedProvider&lt;Person&gt; provider = new CommaDelimitedProvider&lt;Person&gt;(url.getFile()) {\r\n\t\t@Override\r\n\t\tprotected List&lt;Person&gt; doPostFetchResults(List&lt;Person&gt; results,\r\n\t\t\t\tPaginator paginator) {\r\n\t\t\tint end = paginator.getFirstResult()+paginator.getMaxRows();\r\n\t\t\tSystem.out.println(&quot;Fetching results from &quot;+paginator.getFirstResult()+&quot; to &quot;+end);\r\n\t\t\treturn results;\r\n\t\t}\r\n\t};\r\n\tprovider.setRowMapper(new PersonRowMapper());\r\n\treturn provider;\r\n}\r\n<\/pre>\n<\/li>\n<li>\nIf you run this now, with the max rows set to 5, in you will see in that log that we iterate through all the records, but we only fetch the results every 5 rows since the max results is set to 5.<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nFetching results from 0 to 5\r\nPerson [id=6825, name=JUANITA LAMBERT, address=139 MANNING HWY,CLYO,76604, email=mbeasley@everyma1l.biz\r\nPerson [id=5740, name=GREG CABRERA, address=736 GENESSEE BLVD,CORDELE,17433, email=cholder@b1zmail.biz\r\nPerson [id=8599, name=ALISSA WISE, address=205 ALICE RD,CAMILLA,14855, email=theyweb@eyec0de.net\r\nPerson [id=9282, name=SHARON WINTERS, address=955 COHEN PIKE,TYRONE,811, email=jlogan@hotma1l.com\r\nPerson [id=2150, name=KRISTY FRANKS, address=1471 ALEXIS PKWY,BALDWIN,85, email=jgates3@somema1l.com\r\nFetching results from 5 to 10\r\nPerson [id=9927, name=JEFF RICE, address=104 DUNDEE PKWY,HOGANSVILLE,3741, email=diedlots@b1zmail.org\r\nPerson [id=7972, name=TAMARA BRYANT, address=1382 WOGAN BLVD,CITY OF CALHOUN,43790, email=hotworn@everyma1l.us\r\nPerson [id=5824, name=ALISHA YANG, address=716 HOGANS DR,HARDING,58932, email=foundwrong@hotma1l.net\r\nPerson [id=3402, name=JASON NGUYEN, address=527 MICHAEL CRES,FORT STEWART,14664, email=haveothers@ma1l2u.com\r\nPerson [id=3620, name=LINDSEY CABRERA, address=1420 LAZELERE HTS,FORT STEWART,21650, email=askeddreams@b1zmail.com\r\nFetching results from 10 to 15\r\nPerson [id=3511, name=ANTHONY MATHEWS, address=1325 OKEY LN,THUNDERBOLT,63656, email=cfarrell@everyma1l.co.uk\r\nPerson [id=572, name=JARED FORD, address=722 EUCLID RD,FORSYTH,42014, email=ortrying@b1zmail.co.uk\r\nPerson [id=9720, name=AUTUMN WILLIAMS, address=260 HILL PARK,NORCROSS,58355, email=roomwhere@hotma1l.net\r\nPerson [id=3447, name=MARION BROWN, address=739 MIDDLE PATH,MACON,9944, email=hwagner@b1zmail.co.uk\r\nPerson [id=9356, name=HOPE HAYNES, address=1023 COOPERRIDERS CRES,STATENVILLE,34247, email=sacrificeit@eyec0de.com\r\nFetching results from 15 to 20\r\nPerson [id=2259, name=JEANNIE RANDOLPH, address=672 EDISON PATH,CENTERVILLE,48580, email=wornto@eyec0de.net\r\nPerson [id=8264, name=RACHAEL CONLEY, address=1223 STEVENS CT,CARROLLTON,40084, email=smokewhite20@eyec0de.net\r\n<\/pre>\n<\/li>\n<\/ol>\n<h2>Creating a Swing Client<\/h2>\n<p>DataValve provides an interface for data access that can be used by different clients. Lets look at using our provider with a Swing <code>JTable<\/code>.<\/p>\n<ol>\n<li>Start by creating a new class that will be the Swing frame that contains the table and the scroll pane.\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npublic class CsvTableFrame extends JFrame {\r\n\t\r\n\tprivate JTable table;\r\n\tprivate JScrollPane pane;\r\n\r\n\tpublic CsvTableFrame(CommaDelimitedProvider&lt;Person&gt; provider) {\r\n\t\tinitControls();\r\n\t\tinitModel(provider);\r\n\t}\r\n\t\r\n        \/\/here we will create the table model and attach the provider\r\n\tprivate void initModel(CommaDelimitedProvider&lt;Person&gt; provider) {\r\n\t}\r\n\r\n        \/\/construct the gui table and scrollable panel\r\n \tprivate void initControls() {\r\n\t\tsetTitle(&quot;CSV Data&quot;);\r\n\t\tsetSize(400, 400);\r\n\t\tsetDefaultCloseOperation(EXIT_ON_CLOSE);\r\n\t\tsetVisible(true);\r\n\r\n\t\ttable = new JTable();\r\n\t\tpane = new JScrollPane(table);\r\n\t\ttable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);\r\n\t\tgetContentPane().add(pane);\r\n\t}\r\n}\r\n<\/pre>\n<p>This will setup the display for showing a frame with a scrollable table in it.\n<\/li>\n<li>Now we need to implement the <code>initModel<\/code> method which will create a <code>ProviderTableModel<\/code> and attach the provider passed in. The table model class enables us to present our data to the Swing table in a way it understands. The <code>ProviderTableModel<\/code> implements a method which supplies column values to the model from the data supplied by the provider. The columns are defined in the latter half of the method and determines the order used to supply data to the table for each column.\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n        \/\/here we will create the table model and attach the provider\r\n\tprivate void initModel(CommaDelimitedProvider&lt;Person&gt; provider) {\r\n\t\tProviderTableModel&lt;Person&gt; model = new ProviderTableModel&lt;Person&gt;(\r\n\t\t\t\tprovider) {\r\n\r\n\t\t\t@Override\r\n\t\t\tprotected Object getColumnValue(Person person, int column) {\r\n\t\t\t\tswitch (column) {\r\n\t\t\t\tcase 0:\r\n\t\t\t\t\treturn person.getId();\r\n\t\t\t\tcase 1:\r\n\t\t\t\t\treturn person.getName();\r\n\t\t\t\tcase 2:\r\n\t\t\t\t\treturn person.getEmail();\r\n\t\t\t\tcase 3:\r\n\t\t\t\t\treturn person.getAddressText();\r\n\t\t\t\tdefault:\r\n\t\t\t\t\tthrow new RuntimeException(\r\n\t\t\t\t\t\t\t&quot;Unexpected column for person object &quot; + column);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t};\r\n\r\n\t\t\/\/add the columns to the model\r\n\t\tmodel.addColumn(&quot;Id&quot;);\r\n\t\tmodel.addColumn(&quot;Name&quot;);\r\n\t\tmodel.addColumn(&quot;Email&quot;);\r\n\t\tmodel.addColumn(&quot;Address&quot;);\r\n\t\t\r\n\t\t\/\/assign this model to the table\r\n\t\ttable.setModel(model);\r\n\t}\r\n<\/pre>\n<\/li>\n<li>The last piece we need to change is in the <code>main<\/code> method where we will create our provider and then pass it into the creation of our Swing frame.\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npublic static void main(String[] args) {\r\n\t\t\r\n\tCommaDelimitedProvider&lt;Person&gt; provider;\r\n\r\n\tprovider = ProviderFactory.createProvider();\r\n\t\t\r\n\tnew CsvTableFrame(provider);\r\n\r\n}\r\n<\/pre>\n<p>Creating the <code>CsvTableFrame<\/code> initializes and shows the Swing window.\n<\/li>\n<li>If we run this now, we will get a Swing Window which contains a table with our data in it.\n<div class=\"contentBox alignCenter\">\n<a href=\"http:\/\/www.andygibson.net\/blog\/wp-content\/uploads\/2010\/07\/csv_swing_table.png\" target=\"_blank\"><img loading=\"lazy\" src=\"http:\/\/www.andygibson.net\/blog\/wp-content\/uploads\/2010\/07\/csv_swing_table-300x163.png\" alt=\"CSV Swing Table DataValve\" title=\"CSV Swing Table DataValve\" width=\"300\" height=\"163\" border=0\/><\/a>\n<\/div>\n<\/li>\n<\/ol>\n<p>The model controls the flow of the data and even includes built-in caching and look-ahead loading so no matter how big your CSV dataset is, there is no long delay while the data is loaded and converted to Java objects.  In this case, we pass only the provider to the model and the model is responsible for how much data is fetched in each batch. <\/p>\n<p>If you look in the log as you scroll down the list, you will see that it is loading in the data as you scroll. If you go to the end of the list, and start slowly scrolling back up, you won&#8217;t see any more loading messages until you get to the top of the list. This is because the values are cached, but if you have a large dataset, the least recently used items (i.e. those at the top of the table) are removed from the cache and therefore need re-loading when you go back to the start of the list. <\/p>\n<h1>Creating a JSF Client<\/h1>\n<p>The DataValve API allows us to re-use providers with many different clients which we&#8217;ll demonstrate with JSF. <\/p>\n<ol>\n<li>Start by creating a new JSF web application, or use the <a href=\"http:\/\/www.andygibson.net\/blog\/projects\/knappsack\">Knappsack Archetypes<\/a> for Maven to get started quickly. Use the basic archetype so there is no existing application in there or alternative <code>Person<\/code> objects to clash with.<\/li>\n<li>Add the <code>datavalve-dataset<\/code> API and the <code>datavalve-faces<\/code> dependencies to the project,\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n\t\t&lt;dependency&gt;\r\n\t\t\t&lt;groupId&gt;org.fluttercode.datavalve&lt;\/groupId&gt;\r\n\t\t\t&lt;artifactId&gt;datavalve-dataset&lt;\/artifactId&gt;\r\n\t\t\t&lt;version&gt;0.9.0.CR2&lt;\/version&gt;\r\n\t\t&lt;\/dependency&gt;\r\n\t\t&lt;dependency&gt;\r\n\t\t\t&lt;groupId&gt;org.fluttercode.datavalve&lt;\/groupId&gt;\r\n\t\t\t&lt;artifactId&gt;datavalve-faces&lt;\/artifactId&gt;\r\n\t\t\t&lt;version&gt;0.9.0.CR2&lt;\/version&gt;\r\n\t\t&lt;\/dependency&gt;\r\n\r\n<\/pre>\n<\/li>\n<li>For convenience, copy the <code>ProviderFactory.java<\/code>,<code>Person.java<\/code> and <code>PersonRowMapper.java<\/code> classes over to the new project.\n<\/li>\n<li>Create a new class called that will be our backing bean that will hold the dataset the JSF page will go against.\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n@Named(&quot;csvDataset&quot;)\r\n@RequestScoped\r\npublic class CsvDatasetBean {\r\n\t\r\n\tCommaDelimitedDataset&lt;Person&gt; dataset = \r\n\t     new CommaDelimitedDataset&lt;Person&gt;(ProviderFactory.createProvider());\r\n\t\r\n\tpublic CsvDatasetBean() {\r\n\t\t\/\/initialize the dataset settings  \r\n\t\tdataset.setMaxRows(10);\r\n\t}\r\n\t\t\r\n\tpublic CommaDelimitedDataset&lt;Person&gt; getDataset() {\r\n\t\treturn dataset;\r\n\t}\t\r\n}\r\n<\/pre>\n<\/li>\n<li>Now we have created the backing bean pieces, open <code>home.xhtml<\/code> to edit it, and replace the hello world text with the following :\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;\r\n&lt;ui:composition xmlns=&quot;http:\/\/www.w3.org\/1999\/xhtml&quot;\r\n\txmlns:ui=&quot;http:\/\/java.sun.com\/jsf\/facelets&quot;\r\n\txmlns:f=&quot;http:\/\/java.sun.com\/jsf\/core&quot;\r\n\txmlns:h=&quot;http:\/\/java.sun.com\/jsf\/html&quot;\r\n\txmlns:dv=&quot;http:\/\/java.sun.com\/jsf\/composite\/datavalve&quot;\t\r\n\ttemplate=&quot;\/WEB-INF\/templates\/template.xhtml&quot;&gt;\r\n\t&lt;ui:define name=&quot;content&quot;&gt;\r\n\t\t&lt;h:dataTable value=&quot;#{csvDataset.dataset.resultList}&quot; var=&quot;v_person&quot;&gt;\r\n\t\t\t&lt;h:column&gt;\r\n\t\t\t\t&lt;f:facet name=&quot;header&quot;&gt;ID&lt;\/f:facet&gt;\r\n\t\t\t\t&lt;h:outputText value=&quot;#{v_person.id}&quot; \/&gt;\r\n\t\t\t&lt;\/h:column&gt;\r\n\r\n\t\t\t&lt;h:column&gt;\r\n\t\t\t\t&lt;f:facet name=&quot;header&quot;&gt;Name&lt;\/f:facet&gt;\r\n\t\t\t\t&lt;h:outputText value=&quot;#{v_person.name}&quot; \/&gt;\r\n\t\t\t&lt;\/h:column&gt;\r\n\r\n\t\t\t&lt;h:column&gt;\r\n\t\t\t\t&lt;f:facet name=&quot;header&quot;&gt;Email&lt;\/f:facet&gt;\r\n\t\t\t\t&lt;h:outputText value=&quot;#{v_person.email}&quot; \/&gt;\r\n\t\t\t&lt;\/h:column&gt;\r\n\r\n\t\t&lt;\/h:dataTable&gt;\r\n\t\t&lt;h:form&gt;\r\n\t\t\t&lt;dv:simplePaginator paginator=&quot;#{csvDataset.dataset}&quot; \/&gt;\r\n\t\t&lt;\/h:form&gt;\r\n\t&lt;\/ui:define&gt;\r\n&lt;\/ui:composition&gt;\r\n<\/pre>\n<p>This page contains a table that takes the results from <code>#{csvDataset.dataset.resultList}<\/code> and displays the id, name and email fields. The last item wrapped in a form is the <code>datavalve-faces<\/code> default paginator which allows to you scroll across the data. This is provided as part of <code>datavalve-faces<\/code> and the namespace is added at the top of the page.  With JSF 2.0 including support for AJAX, you can have AJAX enabled pagination by setting the attributes on the component.<\/p>\n<div class=\"contentBox alignCenter\">\n<a href=\"http:\/\/www.andygibson.net\/blog\/wp-content\/uploads\/2010\/07\/csv_jsf_table.png\" target=\"_blank\"><img loading=\"lazy\" border=0 src=\"http:\/\/www.andygibson.net\/blog\/wp-content\/uploads\/2010\/07\/csv_jsf_table-300x181.png\" alt=\"CSV JSF Table DataValve\" title=\"CSV JSF Table DataValve\" width=\"300\" height=\"181\" border=0 \/><\/a>\n<\/div>\n<\/li>\n<\/ol>\n<h2>Summary<\/h2>\n<p>This tutorial has shown how to consume csv files in a way that is re-usable across different client applications using DataValve. Alternatively, if you change the implementation of <code>ProviderFactory.createProvider<\/code> to return a different type of provider (i.e. JDBC, ORM, Hibernate) as long is it returns the same kind of <code>Person<\/code> object, your code will run unchanged in the clients you create. Given the simplicity of the DataValve interfaces, it is not hard to see how easy it is to create providers for other file types whether they be text or binary based.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Prerequisites Install Maven Installing DataValve Into Maven While DataValve is mostly used with database driven back ends, this tutorial shows you how DataValve can turn a comma delimited file into a paginated list of objects that the user can page through. We will then use this data provider in a console application, a Swing application [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0},"categories":[64],"tags":[66,78,62,32,81],"_links":{"self":[{"href":"http:\/\/www.andygibson.net\/blog\/wp-json\/wp\/v2\/posts\/1123"}],"collection":[{"href":"http:\/\/www.andygibson.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.andygibson.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.andygibson.net\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/www.andygibson.net\/blog\/wp-json\/wp\/v2\/comments?post=1123"}],"version-history":[{"count":18,"href":"http:\/\/www.andygibson.net\/blog\/wp-json\/wp\/v2\/posts\/1123\/revisions"}],"predecessor-version":[{"id":1229,"href":"http:\/\/www.andygibson.net\/blog\/wp-json\/wp\/v2\/posts\/1123\/revisions\/1229"}],"wp:attachment":[{"href":"http:\/\/www.andygibson.net\/blog\/wp-json\/wp\/v2\/media?parent=1123"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.andygibson.net\/blog\/wp-json\/wp\/v2\/categories?post=1123"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.andygibson.net\/blog\/wp-json\/wp\/v2\/tags?post=1123"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}