I am unable to figure out how to send a list as a parameter to SQL Server Stored Procedure using myBatis
call sp(List<Object>)
I have a stored procedure inside a SQL Server(2012) which takes a parameter of type list.
CREATE TypeTable of Table
(
#FKId IN
#FKId INT
#FKId INT
#FKId INT
#FKId INT
#userName VARCHAR
)
My Stored Procedure call
ALTER PROCEDURE SP(#TypeTableList Typetable READONLY )
AS
BEGIN
/* My DB Operations To Enter New Records and Thier Child Records */
END
MyMapper
<select id="mapperId" parameterType="map" statementType="CALLABLE">
call sp(#{list})
</select>
POJO
public class ListClass {
private Long fk1;
private Long fk2;
private Long fk3;
private Long fk4;
private Long fk5;
private String userName;
public ListClass() {
super();
}
public Long getFk1() {
return fk1;
}
public void setFk1(Long fk1) {
this.fk1 = fk1;
}
public Long getFk2() {
return fk2;
}
public void setFk2(Long fk2) {
this.fk2 = fk2;
}
public Long getFk3() {
return fk3;
}
public void setFk3(Long fk3) {
this.fk3 = fk3;
}
public Long getFk4() {
return fk4;
}
public void setFk4(Long fk4) {
this.fk4 = fk4;
}
public Long getFk5() {
return fk5;
}
public void setFk5(Long fk5) {
this.fk5 = fk5;
}
public String getuserName() {
return userName;
}
public void setuserName(String userName) {
this.userName = userName;
}
}
I have tried using type handler of type array but i always get a exception.
I have not found any resources on creating a custom type handler for ArrayList With SQL Server
Any Help would be appriciated
Thankyou
The create statement you posted didn't work in SQL Server 2017, so I'll show you a simpler example.
DDLs:
create table Users (
id int,
name varchar(20)
);
create type UserTableType as table (
id int,
name varchar(20)
);
create procedure uspAddUsers
#UserTable UserTableType READONLY
as
begin
insert into Users (id, name)
select * from #UserTable
end;
POJO:
public class User {
private Integer id;
private String name;
// getter/setter
}
Mapper method:
#Options(statementType = StatementType.CALLABLE)
#Insert("{call uspAddUsers(#{users,typeHandler=pkg.UserListTypeHandler})}")
void insertUsers(#Param("users") List<User> users);
Note the typeHandler option.
As David Browne pointed out, the driver requires SQLServerDataType as the input, so you may need a type handler that converts the list into a SQLServerDataType.
The below is a simple type handler implementation.
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import com.microsoft.sqlserver.jdbc.SQLServerDataTable;
import com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement;
public class UserListTypeHandler extends BaseTypeHandler<List<User>>{
#Override
public void setNonNullParameter(PreparedStatement ps,
int i, List<User> parameter, JdbcType jdbcType)
throws SQLException {
SQLServerDataTable dataTable = new SQLServerDataTable();
dataTable.addColumnMetadata("id", java.sql.Types.INTEGER);
dataTable.addColumnMetadata("name", java.sql.Types.VARCHAR);
for (User user : parameter) {
dataTable.addRow(user.getId(), user.getName());
}
ps.unwrap(SQLServerPreparedStatement.class)
.setStructured(i, "UserTableType", dataTable);
}
// getNullableResult() won't be used
}
An executable demo tested with...
Microsoft SQL Server 2017 (RTM-CU15) (KB4498951) - 14.0.3162.1 (X64)
mssql-jdbc 7.3.1.jre8-preview
Related
I am creating a small utility on JAVA flink API to learn the functionalities. I am trying to read csv file and just print it and I have developed a POJO class for the structure of the data. When I executed the code, I dont see the right values.(Integers values are replaced with zeros and null values for String. How do I map the datatype for the attributes
My Main Class:
package org.karthick.flinkLab;
import org.apache.flink.api.java.DataSet;
import org.apache.flink.api.java.ExecutionEnvironment;
import javax.xml.crypto.Data;
public class CSVFileRead {
public static void main(String[] args) throws Exception {
System.out.println("--CSV File Reader using Flink's Data Set API--");
ExecutionEnvironment execEnv = ExecutionEnvironment.getExecutionEnvironment();
DataSet<DataModel> csvInput = execEnv.readCsvFile("C:\\Flink\\Data\\IndividualDetails.csv")
.pojoType(DataModel.class);
csvInput.print();
}
}
My Pojo class (DataModel.class)
package org.karthick.flinkLab;
import org.apache.flink.api.java.tuple.Tuple;
import org.apache.flink.api.java.tuple.Tuple12;
import java.io.Serializable;
import java.util.Date;
public class DataModel<T extends Tuple>
extends Tuple12<Integer,String,Date,Integer,String,String,String,String,String,String,Date,String>
implements Serializable
{
public Integer id;
public String government_id;
public Date diagnosed_date;
public Integer age;
public String detected_city;
public String detected_district;
public String detected_state;
public String nationality;
public String current_status;
public Date status_change_date;
public String notes;
public DataModel() {};
public String getNotes() {
return notes;
}
public Date getStatus_change_date() {
return status_change_date;
}
public String getCurrent_status() {
return current_status;
}
public String getNationality() {
return nationality;
}
public String getDetected_state() {
return detected_state;
}
public String getDetected_district() {
return detected_district;
}
public String getDetected_city() {
return detected_city;
}
public String gender ;
public Date getDiagnosed_date() {
return diagnosed_date;
}
public String getGender() {
return gender;
}
public Integer getAge() {
return age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getGovernment_id() {
return government_id;
}
public void setGovernment_id(String government_id) {
this.government_id = government_id;
}
}
When I executed the main method, I dont see the proper values. Sample result
(0,,Tue May 19 16:50:38 IST 2020,0,,,,,,,Tue May 19 16:50:38 IST 2020,)
where as I expect something like
(2777,AP,Tue May 19 16:50:38 IST 2020,0,A,B,C,D,E,F,Tue May 19 16:50:38 IST 2020,G)
What could be missing here?
You are missing the column mapping from CSV to POJO. Adding the mapping will work. The mapping of the column names must follow the following two rules:
The column names should be exactly the same names as in POJO.
The order of the columns in the mapping should be exactly the same as in the CSV file.
You can define the mapping as follows:
DataSet<DataModel> csvInput = execEnv.readCsvFile("C:\\Flink\\Data\\IndividualDetails.csv")
.pojoType(DataModel.class, "id", "age",.........);
It should have thrown error but it hasn't. It could be a bug
I feel like this is a simple problem, but none of the things i tried work for me. I have an enum, the reason i have string constructor is because Java doesn't allow enum to be numerical..I tried AA, AB, 2C directly without string constructor but that gives an error. Note that for the existing enum i am adding C("2C").
public enum TestEnum{
AA("AA"), AB("AB"), C("2C");
private String display;
private TestEnum( String display ) {
this.display = display;
}
public String toString() {
return display;
}
public String getDisplay() {
return display;
}
public void setDisplay( String display ) {
this.display = display;
}
public String getName() {
return display;
}
Now i have a mybatis mapper which does a merge this is existing and one of the param to the mapper is TestEnum. Until now this worked fine since enum value and string value are same, but i added C("2C"). Now i want to insert 2C to the table using mybaits, but it always inserts C.
merge into text t
using (select #{id} as id from dual) d on (d.id = t.id)
when matched then
update set
appId = #{applId},
src = #{testEnum}
testEnum inserts C, so i changed that to #{testEnum.toString()} which gave me a there is no getter for property name toString() error. I tried #{testEnum.display} and #{testEnum.name} both of them still inserts C whereas i want it to insert 2C. Do you guys know an easier way of handling this?
I don't want to change the model object to pass String rather than TestEnum because this object is being used in many places.Is there a way this can be done in the mybatis mapper without changing model object?
Thanks for your help :)
What you need is a TypeHandler
First, add a static method to your TestEnum to return a TestEnum given a display string:
public static TestEnum fromDisplay(String display){
for (TestEnum v : TestEnum.values()){
if (v.getDisplay().equals(display)){
return v;
}
}
return null;
}
Then use it to create your TypeHandler:
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
public class TestEnumTypeHandler extends BaseTypeHandler<TestEnum> {
#Override
public void setNonNullParameter(PreparedStatement ps, int i, TestEnum parameter, JdbcType jdbcType)
throws SQLException {
ps.setString(i, parameter.getDisplay());
}
#Override
public TestEnum getNullableResult(ResultSet rs, String columnName) throws SQLException {
return TestEnum.fromDisplay(rs.getString(columnName));
}
#Override
public TestEnum getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return TestEnum.fromDisplay(rs.getString(columnIndex));
}
#Override
public TestEnum getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return TestEnum.fromDisplay(cs.getString(columnIndex));
}
}
Finally, register your TypeHandler in your mybatis xml:
<typeHandlers>
<typeHandler handler="blah.blah.TestEnumTypeHandler "/>
</typeHandlers>
In addition to #Malt Answer:
The reason why what you are trying doesn't works it's the MyBatis EnumTypeHandler by default sets name() value of the method and is marked with final so you cannot override it:
EnumTypeHandler.class (Line 38 to 44):
#Override
public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
if (jdbcType == null) {
ps.setString(i, parameter.name());
} else {
ps.setObject(i, parameter.name(), jdbcType.TYPE_CODE); // see r3589
}
}
Otherwise, the enum is created from the method valueOf(type, name) which also uses the name of the enum.
#Override
public E getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
String s = rs.getString(columnIndex);
return s == null ? null : Enum.valueOf(type, s);
}
#Override
public E getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
String s = cs.getString(columnIndex);
return s == null ? null : Enum.valueOf(type, s);
}
So definitely, you need to use a typeHandler specific to handle your enum which has a specific behaviour, but I would extends directly EnumTypeHandler in specific enum type handlers, instead of BaseTypeHandler (Malt answer), because you could reuse some functionality (not in your case, but maybe in others) so it handles a general enum behaviour.
You do not need to write any custom TypeHandler if you want to insert the value of your Enum.
The only one thing you need to do is to specify the getter method's name in your MyBatis insert.
Example:
SQL:
CREATE TABLE demo
(
id BIGINT,
value VARCHAR(10),
status CHAR(1)
);
MyBatis mapper:
#Update("UPDATE demo SET status = #{status.value} WHERE id= #{uuid}")
long updateStatus(#Param("status") Status status, #Param("uuid") String uuid);
And the Java Enum:
public enum Status {
ACTIVE("A"),
INACTIVE("I");
Status(final String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
In your case you can use src = #{testEnum.display} in your SQL.
So the thing that i want to happen, is making the tableview update the data in the database after editing it. I wanted to use the SetOnEditCommit method here. The cell editing does work, but it never gets updated, with no error either. In the first place im a bit clueless if this method is actually efficient (probably not), since its hard to find some sources for this specific thing. And the sources that i found weren't really helpful. So it would be nice if someone had an idea as to why it doesn't update, or maybe provide an alternate option here.
The mentioned part:
columnType.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<UserDetails, String>>() {
#Override
public void handle(TableColumn.CellEditEvent<UserDetails, String> event) {
updataData();
}
});
tableview.setItems(null);
tableview.setItems(data);
}
public void updataData() {
Connection connection = null;
try {
connection = DriverManager.getConnection("jdbc:mysql://37.128.148.113:3306/FYS", "FYS", "Kcj8g87~");
Statement con = connection.createStatement();
//connection
TablePosition pos = tableview.getSelectionModel().getSelectedCells().get(0);
int row = pos.getRow();
TableColumn col = pos.getTableColumn();
String data1 = (String) col.getCellObservableValue(row).getValue();
//cell
UserDetails row1 = tableview.getSelectionModel().getSelectedItem();
c1 = row1.getId();
//row
//tableview variables
con.execute("UPDATE gevonden_bagage SET type = 'data1' WHERE koffer_id = 'c1' ");
//Query
} catch (SQLException ex) {
System.err.println("Error" + ex);
}
}
//get connection, get celldata, get id data from first row, update cell with selected id
full controller class:
package simple;
import java.net.URL;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ResourceBundle;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TablePosition;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
/**
*
* #author admin
*/
public class FXMLUserController extends SimpleController implements Initializable {
#FXML
public TableView<UserDetails> tableview;
#FXML
public TableColumn<UserDetails, String> columnId;
#FXML
public TableColumn<UserDetails, String> columnType;
#FXML
public TableColumn<UserDetails, String> columnKleur;
#FXML
public TableColumn<UserDetails, String> columnLuchthaven;
#FXML
public TableColumn<UserDetails, String> columnKenmerken;
#FXML
public TableColumn<UserDetails, String> columnStatus;
#FXML
public TableColumn<UserDetails, String> columnDatum;
#FXML
private Button btnLoad;
//declare observable list for database data
private ObservableList<UserDetails> data;
private DbConnection dc;
String c1;
#FXML
//strings for getRow method
#Override
public void initialize(URL url, ResourceBundle rb) {
dc = new DbConnection();
loadDataFromDatabase();
}
#FXML
public void loadDataFromDatabase() {
try {
Connection conn = dc.Connect();
data = FXCollections.observableArrayList();
// Execute query and store result in a resultset
ResultSet rs = conn.createStatement().executeQuery("SELECT * FROM gevonden_bagage");
while (rs.next()) {
//get strings
data.add(new UserDetails(rs.getString(1), rs.getString(2), rs.getString(3), rs.getString(4), rs.getString(5),
rs.getString(6), rs.getString(7)));
}
} catch (SQLException ex) {
System.err.println("Error" + ex);
}
//Set cell values to tableview.
tableview.setEditable(true);
tableview.getSelectionModel().setCellSelectionEnabled(true);
columnType.setCellFactory(TextFieldTableCell.forTableColumn());
columnKleur.setCellFactory(TextFieldTableCell.forTableColumn());
columnLuchthaven.setCellFactory(TextFieldTableCell.forTableColumn());
columnKenmerken.setCellFactory(TextFieldTableCell.forTableColumn());
columnStatus.setCellFactory(TextFieldTableCell.forTableColumn());
columnDatum.setCellFactory(TextFieldTableCell.forTableColumn());
//makes columns editable
columnId.setCellValueFactory(new PropertyValueFactory<>("id"));
columnType.setCellValueFactory(new PropertyValueFactory<>("type"));
columnKleur.setCellValueFactory(new PropertyValueFactory<>("kleur"));
columnLuchthaven.setCellValueFactory(new PropertyValueFactory<>("luchthaven"));
columnKenmerken.setCellValueFactory(new PropertyValueFactory<>("kenmerken"));
columnStatus.setCellValueFactory(new PropertyValueFactory<>("status"));
columnDatum.setCellValueFactory(new PropertyValueFactory<>("datum"));
columnType.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<UserDetails, String>>() {
#Override
public void handle(TableColumn.CellEditEvent<UserDetails, String> event) {
updataData();
}
});
tableview.setItems(null);
tableview.setItems(data);
}
public void updataData() {
Connection connection = null;
try {
connection = DriverManager.getConnection("jdbc:mysql://37.128.148.113:3306/FYS", "FYS", "Kcj8g87~");
Statement con = connection.createStatement();
//connection
TablePosition pos = tableview.getSelectionModel().getSelectedCells().get(0);
int row = pos.getRow();
TableColumn col = pos.getTableColumn();
String data1 = (String) col.getCellObservableValue(row).getValue();
//cell
UserDetails row1 = tableview.getSelectionModel().getSelectedItem();
c1 = row1.getId();
//row
//tableview variables
con.execute("UPDATE gevonden_bagage SET type = 'data1' WHERE koffer_id = 'c1' ");
//Query
} catch (SQLException ex) {
System.err.println("Error" + ex);
}
}
//get connection, get celldata, get id data from first row, update cell with selected id
#FXML
public void getRow() {
TablePosition pos = tableview.getSelectionModel().getSelectedCells().get(0);
int row = pos.getRow();
TableColumn col = pos.getTableColumn();
// this gives the value in the selected cell:
String data1 = (String) col.getCellObservableValue(row).getValue();
System.out.println(data1);
//CURRENTLY UNUSED METHOD
}
}
Model class:
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
/**
*
* #author admin
*/
public class UserDetails {
private final StringProperty id;
private final StringProperty type;
private final StringProperty kleur;
private final StringProperty luchthaven;
private final StringProperty kenmerken;
private final StringProperty status;
private final StringProperty datum;
//Default constructor
public UserDetails(String id, String type, String kleur, String luchthaven, String kenmerken, String status, String datum) {
this.id = new SimpleStringProperty(id);
this.type = new SimpleStringProperty(type);
this.kleur = new SimpleStringProperty(kleur);
this.luchthaven = new SimpleStringProperty(luchthaven);
this.kenmerken = new SimpleStringProperty(kenmerken);
this.status = new SimpleStringProperty(status);
this.datum = new SimpleStringProperty(datum);
}
//getters
public String getId() {
return id.get();
}
public String getType() {
return type.get();
}
public String getKleur() {
return kleur.get();
}
public String getLuchthaven() {
return luchthaven.get();
}
public String getKenmerken() {
return kenmerken.get();
}
public String getStatus() {
return status.get();
}
public String getDatum() {
return datum.get();
}
//setters
public void setId(String value) {
id.set(value);
}
public void setType(String value) {
type.set(value);
}
public void setKleur(String value) {
kleur.set(value);
}
public void setLuchthaven(String value) {
luchthaven.set(value);
}
public void setKenmerken(String value) {
kenmerken.set(value);
}
public void setStatus(String value) {
status.set(value);
}
public void setDatum(String value) {
datum.set(value);
}
//property values
public StringProperty idProperty() {
return id;
}
public StringProperty typeProperty() {
return type;
}
public StringProperty kleurProperty() {
return kleur;
}
public StringProperty luchthavenProperty() {
return luchthaven;
}
public StringProperty kenmerkenProperty() {
return kenmerken;
}
public StringProperty statusProperty() {
return status;
}
public StringProperty datumProperty() {
return datum;
}
}
From the TableView documentation:
By default the TableColumn edit commit handler is non-null, with a
default handler that attempts to overwrite the property value for the
item in the currently-being-edited row. It is able to do this as the
Cell.commitEdit(Object) method is passed in the new value, and this is
passed along to the edit commit handler via the CellEditEvent that is
fired. It is simply a matter of calling
TableColumn.CellEditEvent.getNewValue() to retrieve this value.
It is very important to note that if you call
TableColumn.setOnEditCommit(javafx.event.EventHandler) with your own
EventHandler, then you will be removing the default handler. Unless
you then handle the writeback to the property (or the relevant data
source), nothing will happen.
So the problem is that by setting the onEditCommit on columnType, you remove the default handler that actually updates typeProperty in the UserDetails instance. Consequently
String data1 = (String) col.getCellObservableValue(row).getValue();
gives the old value, and your update to the database won't change anything.
Additionally, you have errors in the way you create the SQL statement. You are making the id in the WHERE clause the literal value 'c1' (instead of the value contained in the variable c1, and similarly setting the value of type to the literal value 'data1', instead of the value in the variable data1.
Here is a fix, along with some simplification of the code and some better practices for avoiding SQL injection attacks:
columnType.setOnEditCommit(event -> {
UserDetails user = event.getRowValue();
user.setType(event.getNewValue());
updateData("type", event.getNewValue(), user.getId());
});
and then
private void updateData(String column, String newValue, String id) {
// btw it is way better to keep the connection open while the app is running,
// and just close it when the app shuts down....
// the following "try with resources" at least makes sure things are closed:
try (
Connection connection = DriverManager.getConnection("jdbc:mysql://37.128.148.113:3306/FYS", "FYS", "Kcj8g87~");
PreparedStatement stmt = connection.prepareStatement("UPDATE gevonden_bagage SET "+column+" = ? WHERE koffer_id = ? ");
) {
stmt.setString(1, newValue);
stmt.setString(2, id);
stmt.execute();
} catch (SQLException ex) {
System.err.println("Error");
// if anything goes wrong, you will need the stack trace:
ex.printStackTrace(System.err);
}
}
I have started with parse to store the data of my class. I have followed parse guide and tutorials and tried to implement the code. Unfortunately, the objects of class are not getting saved in parse data browser. When I see the data in browser just one object id is shown not the columns of name, desc and qty of my item class. I have created class in dashboard also created columns respective to my data. Unable to get the solution as I am new to android and parse.
Here is my code
Item class
package com.example.owner.newstock;
import com.parse.ParseClassName;
import com.parse.ParseObject;
#ParseClassName("Item")
public class Item extends ParseObject {
public int id;
public String item_name;
public String item_desc;
public String item_qty;
public Item(){}
public Item(int id, String item_name, String item_desc, String item_qty) {
super();
this.item_name = item_name;
this.item_desc = item_desc;
this.item_qty = item_qty;
}
public Item(String item_name, String item_desc, String item_qty){
this.item_name = item_name;
this.item_desc=item_desc;
this.item_qty = item_qty;
}
public int getID(){
return id;
}
public void setID(int id){
this.id= id;
}
public String getItem_name(){
return getString(item_name);
}
public void setItem_name(String item_name)
{
put("item_name", item_name);
}
public String getItem_desc()
{
return getString(item_desc);
}
public void setItem_desc(String item_desc)
{
put("item_desc", item_desc);
}
public String getItem_qty()
{
return getString (item_qty);
}
public void setItem_qty(String item_qty){
put("item_qty", item_qty);
}
}
code of parse in main activity
ParseObject.registerSubclass(Item.class);
Parse.initialize(this, "Kw0dyUgLoqv24QdLE30mvFBVclEzLHRGtR2hQVHA", "5BWc3bAd60EgqU0sFIj31mMYYg7OIX9WKgC0a6oP");
ParseAnalytics.trackAppOpened(getIntent());
code to save the objects
Item i = new Item();
i.setItem_name(item_name);
i.setItem_desc(item_desc);
i.setItem_qty(item_qty);
i.saveInBackground();
Am I missing something?
Rather than creating an item class that extends ParseObject, set up a ParseObject variable, as follows:
ParseObject item = new ParseObject("Item");
Then put data in as follows:
item.put("quantity", yourQuantityVariable);
item.put("description", yourDescriptionVariable);
item.put("name", yourNameVariable);
To save:
item.saveInBackground();
To retrieve data, make use of querying and the getDataType() methods. Specified on https://parse.com/docs/android/guide#objects and https://parse.com/docs/android/guide#queries
Any idea on how to define indexes on data stored with Akiban's Persistit key/value store?
There isn't first class/API support for secondary indexes in Persistit. That isn't to say you can't create indexes though!
What is an index? In practice, all an index contains is another copy of the data. For example, in a relational database with a users table, an index on the first_name column would allow efficient look-up by first name. That can be achieved by storing an additional copy of the first name with the primary identifier to create a "link" back to the main row.
Here's an isolated example of that:
import com.persistit.*;
import com.persistit.exception.*;
import java.io.*;
import java.util.*;
public class IndexDemo implements AutoCloseable
{
public static class User implements Serializable
{
public int id;
public String firstName;
public String lastName;
public User() {
}
public User(int id, String firstName, String lastName) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
}
#Override
public String toString() {
return String.format("User(%d, %s, %s)", id, firstName, lastName);
}
}
private final Persistit db;
public IndexDemo() throws PersistitException {
Configuration c = new Configuration();
c.getBufferPoolMap().get(16384).setCount(32);
c.getVolumeList().add(new VolumeSpecification(
"IndexDemo.vol,create,pageSize:16384,initialPages:5,extensionPages:5,maximumPages:100"
));
this.db = new Persistit(c);
}
#Override
public void close() throws PersistitException {
db.close();
}
public Exchange userEx() throws PersistitException {
return db.getExchange("IndexDemo", "users", true);
}
public Exchange firstNamesEx() throws PersistitException {
return db.getExchange("IndexDemo", "firstNames", true);
}
// Save the user, both primary and secondary firstName index
public void saveUser(User u) throws PersistitException {
Exchange ex = userEx();
// Primary entries: key of ID and value of full User
ex.getKey().append(u.id);
ex.getValue().put(u);
ex.store();
// First name index: key of (name,ID)
ex = firstNamesEx();
ex.append(u.firstName).append(u.id);
ex.store();
}
// Look-up the user by ID
public User userByID(int id) throws PersistitException {
Exchange ex = userEx();
// Construct and fetch our key
ex.getKey().append(id);
ex.fetch();
// Careful: may not exist
if(!ex.getValue().isDefined()) {
return null;
}
// Otherwise get it from the value
return (User)ex.getValue().get();
}
// Index scan for users with firstName, look-up and return all matches
public List<User> usersByFirstName(String firstName) throws PersistitException {
List<User> users = new ArrayList<>();
Exchange ex = firstNamesEx();
// Iterate over only entires matching firstName
ex.append(firstName).append(Key.BEFORE);
while(ex.next()) {
// Index to second component (id) and decode
int id = ex.getKey().indexTo(1).decodeInt();
// And lookup the user
users.add(userByID(id));
}
return users;
}
public static void main(String[] args) throws PersistitException {
try(final IndexDemo demo = new IndexDemo()) {
System.out.println("No Transaction:");
runDemo(demo);
}
try(final IndexDemo demo = new IndexDemo()) {
System.out.println("Transaction:");
demo.db.getTransaction().run(new TransactionRunnable() {
#Override
public void runTransaction() throws PersistitException {
runDemo(demo);
}
});
}
}
public static void runDemo(IndexDemo demo) throws PersistitException {
demo.saveUser(new User(1, "John", "Doe"));
demo.saveUser(new User(2, "John", "Smith"));
demo.saveUser(new User(3, "Sally", "Jones"));
System.out.println(" User 1: " + demo.userByID(1));
System.out.println(" User 10: " + demo.userByID(10));
System.out.println(" Users named John:");
for(User u : demo.usersByFirstName("John")) {
System.out.println(" " + u);
}
}
}
Running yields this output:
No Transaction:
User 1: User(1, John, Doe)
User 10: null
Users named John:
User(1, John, Doe)
User(2, John, Smith)
Transaction:
User 1: User(1, John, Doe)
User 10: null
Users named John:
User(1, John, Doe)
User(2, John, Smith)
There isn't too much going on:
User POJO with a few attributes
Basic Persistit configuration and start-up
Helpers for saving, look-up by primary/ID and scan by first name
Demo usage of all the helpers
Main runs the demo both inside and outside of a transaction
All the pieces are there for building something extremely simple, like this demo, to something very sophisticated, like a complete SQL server.