I am facing trouble in adding data using SQLite database - android-studio

I am trying to insert some information of cars using 3 EditTexts in my xml page and by creating table SQLite database and an error occurs in logcat:
Process: com.example.sqlite, PID: 7264
java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.Resources android.content.Context.getResources()' on a null object reference
at android.widget.Toast.<init>(Toast.java:102)
at android.widget.Toast.makeText(Toast.java:259)
at com.example.sqlite.MyDatabaseHelper.addCar(MyDatabaseHelper.kt:58)
at com.example.sqlite.AddActivity$onCreate$1.onClick(AddActivity.kt:15)
at android.view.View.performClick(View.java:5198)
at android.view.View$PerformClick.run(View.java:21147)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
here is my Database Creation:
private val context:Context? = null
private const val TABLE_NAME = "my_CarShow"
private const val COLUMN_ID = "_id"
private const val COLUMN_NAME = "car_name"
private const val COLUMN_TYPE = "car_type"
private const val COLUMN_MODEL = "car_model"
const val DATABASE_NAME: String = "CarShow.db"
const val DATABASE_VERSION: Int = 1
class MyDatabaseHelper(context: Context?) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {
override fun onCreate(db: SQLiteDatabase?) {
val query = "CREATE TABLE " + TABLE_NAME +
" (" + COLUMN_ID + " INTEGER PRIMARY KEY," +
COLUMN_NAME + " TEXT," +
COLUMN_TYPE + " TEXT," +
COLUMN_MODEL + " INTEGER)"
db!!.execSQL(query)
}
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
db!!.execSQL("DROP TABLE IF EXISTS $TABLE_NAME")
onCreate(db)
}
fun addCar(name:String, type:String, model:String){
var db = this.writableDatabase
var cv = ContentValues()
cv.put(COLUMN_NAME,name)
cv.put(COLUMN_TYPE,type)
cv.put(COLUMN_MODEL,model)
var result = db.insert(TABLE_NAME,null, cv)
if (result == -1L){
Toast.makeText(context,"Failed",Toast.LENGTH_SHORT).show()
}
else{
Toast.makeText(context,"Added Successfully",Toast.LENGTH_SHORT).show()
}
}
}
and here is the activity that the data must be iserted or added :
class AddActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_add)
add_button.setOnClickListener {
val myDb = MyDatabaseHelper(this)
myDb.addCar(name_input.text.toString(),
type_input.text.toString(),
model_input.text.toString())
}
}
}
what can I do to solve this error?
Thanks for your attention

Related

Kotlin Edit Text as Integer

I want to make an app where you have to guess two numbers that eaquals a random number. But my app crash at the start because of java.lang.NumberFormatException: For input string: ""
val intNumber1 = etNumber1.text.toString().toInt() is the line that causes the crash. But I dont know what to do.
Here is my main activity:
//Buttons und Text initialisieren
private lateinit var btnCheck: Button
private lateinit var tvRndNumber: TextView
private lateinit var etNumber1: EditText
private lateinit var etNumber2: EditText
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//Buttons und Text verknüpfen
btnCheck = findViewById(R.id.checkButton)
tvRndNumber = findViewById(R.id.randomNumber)
etNumber1 = findViewById(R.id.inputNumber1)
etNumber2 = findViewById(R.id.inputNumber2)
val intNumber1 = etNumber1.text.toString().toInt()
val intNumber2 = etNumber2.text.toString().toInt()
fun getRandomNumber(): Int {
return Random.nextInt(0,100)
}
var intRdnNumber = getRandomNumber()
tvRndNumber.text = intRdnNumber.toString()
fun checkNumbers(num1: Int, num2: Int){
if (num1 + num2 == intRdnNumber) {
tvRndNumber.text = "Richtig"
}
else {
Toast.makeText(this,"Probier es nochmal", Toast.LENGTH_LONG).show()
}
}
btnCheck.setOnClickListener {
checkNumbers(intNumber1, intNumber2)
}
Try like the following
//....
val intNumber1 = etNumber1.text.toString()
val intNumber2 = etNumber2.text.toString()
//.....
btnCheck.setOnClickListener {
if(intNumber1.isNotEmpty && intNumber2.isNotEmpty{
checkNumbers(intNumber1.toInt(), intNumber2.toInt())
}else{
// input filed is empty
// show message or do nothing
}
}
val intNumber1 = if(etNumber1.text.isNotEmpty()) {
etNumber1.text.toString().toInt()
} else {
0
}
val intNumber2 = if(etNumber2.text.isNotEmpty()) {
etNumber2.text.toString().toInt()
} else {
0
}

Kotlin string replace function not working for me?

Hello everyone please help me i am try to modify link but it's not working. it's working on java but recently i convert java to kotlin and getting this error.
am trying to change my link http://www.offertoro.com/click_track/api?offer_id=419393&pub_id=14&pub_app_id=5&USER_ID=[USER_ID]&GAID=[your_GAID] in [USER_ID] with current login user email but getting error.
Check Screenshot
Screenshot
Error
None of the following functions can be called with the arguments supplied.
CharSequence.replace(Regex, String) defined in kotlin.text
String.replace(String, String, Boolean = ...) defined in kotlin.text
My code
fun modifyOfferLink() {
val id = mAuth!!.currentUser!!.email
// Modifying Offer Link Acording to Offer Partner
when (partner) {
"ogads" -> Finallink = link + "&aff_sub5=" + mAuth!!.currentUser!!.email
"offertoro" -> Finallink = link.replace("[USER_ID]", mAuth!!.currentUser!!.email)
"none" -> {
Finallink = link!!.replace("[USER_ID]", mAuth!!.currentUser!!.email)
}
else -> Finallink = link.replace("[USER_ID]", mAuth!!.currentUser!!.email)
}
}
OfferActivity.kt
package com.sgamer.creditsk.Activity
import android.content.Intent
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.View
import android.view.Window
import android.view.WindowManager
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
import com.google.firebase.auth.FirebaseAuth
import com.google.gson.Gson
import com.google.gson.JsonObject
import com.loopj.android.http.AsyncHttpClient
import com.loopj.android.http.AsyncHttpResponseHandler
import com.loopj.android.http.RequestParams
import com.sgamer.creditsk.Activity.AndyConstants.ServiceType
import com.sgamer.creditsk.Activity.OfferDetailsActivity
import com.sgamer.creditsk.R
import com.sgamer.creditsk.Utils.*
import cz.msebera.android.httpclient.Header
import org.json.JSONArray
import org.json.JSONException
import org.json.JSONObject
class OfferDetailsActivity constructor() : AppCompatActivity() {
var Finallink: String? = null
var package_id: String? = null
var uniq_id: String? = null
var offerid: String? = null
var app_name: String? = null
var description: String? = null
var icon_url: String? = null
var bg_image_url: String? = null
var amount: String? = null
var OriginalAmount: String? = null
var link: String? = null
var partner: String? = null
var insTitle: String? = null
var first_text: String? = null
var second_text: String? = null
var third_text: String? = null
var fourth_text: String? = null
var webview: Boolean? = null
var ClickId: String? = null
var ctx: OfferDetailsActivity? = null
var later: TextView? = null
var status_image: ImageView? = null
var mAuth: FirebaseAuth? = null
private val bannerAdManager: BannerAdManager_SK? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_offer_details)
val toolbar: Toolbar = findViewById<View>(R.id.toolbar) as Toolbar
val adContainer: LinearLayout = findViewById<View>(R.id.adView) as LinearLayout
setSupportActionBar(toolbar)
val bannerAdManager_SK: BannerAdManager_SK = BannerAdManager_SK(this#OfferDetailsActivity, adContainer)
bannerAdManager_SK.BannerAds()
ctx = this
mAuth = FirebaseAuth.getInstance()
getSupportActionBar()!!.setTitle(R.string.offer_details)
getSupportActionBar()!!.setDisplayHomeAsUpEnabled(true)
getSupportActionBar()!!.setBackgroundDrawable(ColorDrawable(getResources().getColor(android.R.color.transparent)))
getSupportActionBar()!!.setElevation(0f)
if (Build.VERSION.SDK_INT >= 21) {
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
}
changeStatusBarColor()
initViews()
modifyOfferLink()
}
private fun changeStatusBarColor() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val window: Window = getWindow()
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
window.setStatusBarColor(Color.TRANSPARENT)
}
}
fun initViews() {
val title: TextView = findViewById(R.id.title)
val desc: TextView = findViewById(R.id.description)
val instructionsTitle: TextView = findViewById(R.id.instructions)
val first: TextView = findViewById(R.id.first)
val second: TextView = findViewById(R.id.second)
val third: TextView = findViewById(R.id.third)
val fourth: TextView = findViewById(R.id.fourth)
val des: TextView = findViewById(R.id.des)
val complete_button: TextView = findViewById(R.id.complete_button)
val button: TextView = findViewById(R.id.button)
later = findViewById(R.id.later)
val comSpace: LinearLayout = findViewById(R.id.comSpace)
val offer_icon: ImageView = findViewById(R.id.offer_icon)
val bg_image: ImageView = findViewById(R.id.bg_image)
status_image = findViewById(R.id.status_image)
uniq_id = getIntent().getStringExtra("uniq_id")
offerid = getIntent().getStringExtra("offerid")
app_name = getIntent().getStringExtra("app_name")
package_id = getIntent().getStringExtra("package_id")
description = getIntent().getStringExtra("description")
icon_url = getIntent().getStringExtra("icon_url")
bg_image_url = getIntent().getStringExtra("bg_image_url")
amount = getIntent().getStringExtra("amount")
OriginalAmount = getIntent().getStringExtra("OriginalAmount")
link = getIntent().getStringExtra("link")
partner = getIntent().getStringExtra("partner")
first_text = getIntent().getStringExtra("first_text")
insTitle = getIntent().getStringExtra("instructionsTitle")
second_text = getIntent().getStringExtra("second_text")
third_text = getIntent().getStringExtra("third_text")
fourth_text = getIntent().getStringExtra("fourth_text")
webview = getIntent().getBooleanExtra("webview", false)
if (getIntent().hasExtra("description")) {
des.setText(getIntent().getStringExtra("description"))
} else {
des.setText(getIntent().getStringExtra("description"))
}
title.setText(app_name)
desc.setText(getString(R.string.earn) + " " + amount + " " + getString(R.string.app_currency) + " " + getString(R.string.on_this_offer))
Glide.with(this).load(icon_url)
.apply(RequestOptions().placeholder(R.drawable.placeholder_image).error(R.drawable.placeholder_image))
.into(offer_icon)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
comSpace.setElevation(20f)
}
instructionsTitle.setText(insTitle)
first.setText(first_text)
second.setText(second_text)
third.setText(third_text)
fourth.setText(fourth_text)
complete_button.setText(getResources().getString(R.string.complete_offer))
if (!bg_image_url!!.isEmpty()) {
Glide.with(this).load(bg_image_url).into(bg_image)
} else {
}
// On click Listners
later!!.setOnClickListener(object : View.OnClickListener {
public override fun onClick(view: View) {
finish()
}
})
complete_button.setOnClickListener(object : View.OnClickListener {
public override fun onClick(view: View) {
if (!App.isVPNConnected()) {
addoffer(amount, app_name + " Offer Credit", offerid)
}
AppUtils.parse(this#OfferDetailsActivity, Finallink)
}
})
button.setOnClickListener(object : View.OnClickListener {
public override fun onClick(view: View) {
val launchIntent: Intent? = getPackageManager().getLaunchIntentForPackage((package_id)!!)
startActivity(launchIntent)
}
})
isAppExist
if (isAppExist) {
complete_button.setVisibility(View.GONE)
button.setVisibility(View.VISIBLE)
} else {
button.setVisibility(View.GONE)
complete_button.setVisibility(View.VISIBLE)
}
}
fun addoffer(points: String?, Activity: String?, offerid: String?) {
val client: AsyncHttpClient = AsyncHttpClient()
val params: RequestParams = RequestParams()
val jsObj: JsonObject = Gson().toJsonTree(API()) as JsonObject
jsObj.addProperty("method_name", "user_offeradd")
jsObj.addProperty("offer_id", offerid)
jsObj.addProperty("email", mAuth!!.getCurrentUser()!!.getEmail())
jsObj.addProperty("points", points)
jsObj.addProperty("firebase_id", mAuth!!.getCurrentUser()!!.getUid())
jsObj.addProperty("Activity", Activity)
params.put("data", API.toBase64(jsObj.toString()))
client.post(Javaaescipher.decrypt(), params, object : AsyncHttpResponseHandler() {
public override fun onSuccess(statusCode: Int, headers: Array<Header>, responseBody: ByteArray) {
Log.d("Response", String(responseBody))
val res: String = String(responseBody)
try {
val jsonObject: JSONObject = JSONObject(res)
val jsonArray: JSONArray = jsonObject.getJSONArray("ANDROID_REWARDS_APP")
for (i in 0 until jsonArray.length()) {
val `object`: JSONObject = jsonArray.getJSONObject(i)
val success: String = `object`.getString("success")
val msg: String = `object`.getString("msg")
if ((success == "1")) {
// Toast.makeText(OfferDetailsActivity.this, msg, Toast.LENGTH_LONG).show();
} else {
// Toast.makeText(OfferDetailsActivity.this, msg, Toast.LENGTH_LONG).show();
}
}
} catch (e: JSONException) {
e.printStackTrace()
}
}
public override fun onFailure(statusCode: Int, headers: Array<Header>, responseBody: ByteArray, error: Throwable) {
Log.d("error", error.toString())
}
})
}
private val isAppExist: Boolean
private get() {
val pm: PackageManager = getPackageManager()
try {
val info: PackageInfo = pm.getPackageInfo((package_id)!!, PackageManager.GET_META_DATA)
} catch (e: PackageManager.NameNotFoundException) {
return false
}
return true
}
fun modifyOfferLink() {
val id = mAuth!!.currentUser!!.email
// Modifying Offer Link Acording to Offer Partner
when (partner) {
"ogads" -> Finallink = link + "&aff_sub5=" + mAuth!!.currentUser!!.email
"offertoro" -> Finallink = link!!.replace("[USER_ID]", mAuth!!.currentUser!!.email)
"none" -> {
Finallink = link!!.replace("[USER_ID]", mAuth!!.currentUser!!.email)
}
else -> Finallink = link!!.replace("[USER_ID]", mAuth!!.currentUser!!.email)
}
}
}
It is likely there is no sub-string that is exactly [USER_ID]. Are you sure is it not ["USER_ID"] ?
Can you print the value of link before you call the replace. This may let us and you see the problem.

Spark 2 Dataframe Save to Hive - Compaction

I am using spark session to save a data frame to hive table. The code is as below.
df.write.mode(SaveMode.Append).format("orc").insertInto("table")
The data comes to spark from kafka. This can be huge amount of data coming throughout the day. Does , spark dataframe save internally does hive compaction ?. If not what is the best way to do compaction at regular intervals without affecting the table insertions.
In your example you should add partitionBy as data can be in huge amount
df.write..mode(SaveMode.Append).format("orc").partitionBy("age")
OR you can also archive as below
The way I have done this is to first register a temp table in Spark job itself and then leverage the sql method of the HiveContext to create a new table in hive using the data from the temp table. For example if I have a dataframe df and HiveContext hc the general process is:
df.registerTempTable("my_temp_table")
hc.sql("Insert into overwrite table_name PARTITION SELECT a,b, PARTITION_col from my_temp_table")
public class HiveCompaction {
private static SparkConf sparkConf;
private static JavaSparkContext sc;
private static SparkSession sqlContext = springutil.getBean("testSparkSession");
private static HashMap<Object, Object> partitionColumns;
public static void compact(String table, Dataset<Row> dataToCompact) {
logger.info("Started Compaction for - " + table);
if (!partitionColumns.containsKey(table)) {
compact_table_without_partition(table, dataToCompact);
} else {
compact_table_with_partition(table, dataToCompact, partitionColumns);
}
logger.info("Data Overwritten in HIVE table : " + table + " successfully");
}
private static void compact_table_with_partition(String table, Dataset<Row> dataToCompact,
Map<Object, Object> partitionData) {
String[] partitions = ((String) partitionData.get(table)).split(",");
List<Map<Object, Object>> partitionMap = getPartitionsToCompact(dataToCompact, Arrays.asList(partitions));
for (Map mapper : partitionMap) {
// sqlContext.sql("REFRESH TABLE staging.dummy_table");
String query = "select * from " + table + " where " + frameQuery(" and ", mapper);
Dataset<Row> originalTable = sqlContext.sql(query.toString());
if (originalTable.count() == 0) {
dataToCompact.write().mode("append").format("parquet").insertInto(table);
} else {
String location = getHdfsFileLocation(table);
String uuid = getUUID();
updateTable(table, dataToCompact, originalTable, uuid);
String destinationPath = framePath(location, frameQuery("/", mapper), uuid);
sqlContext.sql("Alter table " + table + " partition(" + frameQuery(",", mapper) + ") set location '"
+ destinationPath + "'");
}
}
}
private static void compact_table_without_partition(String table, Dataset<Row> dataToCompact) {
String query = "select * from " + table;
Dataset<Row> originalTable = sqlContext.sql(query.toString());
if (originalTable.count() == 0) {
dataToCompact.write().mode("append").format("parquet").insertInto(table);
} else {
String location = getHdfsFileLocation(table);
String uuid = getUUID();
String destinationPath = framePath(location, null, uuid);
updateTable(table, dataToCompact, originalTable, uuid);
sqlContext.sql("Alter table " + table + " set location '" + destinationPath + "'");
}
}
private static void updateTable(String table, Dataset<Row> dataToCompact, Dataset<Row> originalTable, String uuid) {
Seq<String> joinColumnSeq = getPrimaryKeyColumns();
Dataset<Row> unModifiedRecords = originalTable.join(dataToCompact, joinColumnSeq, "leftanti");
Dataset<Row> dataToInsert1 = dataToCompact.withColumn("uuid", functions.lit(uuid));
Dataset<Row> dataToInsert2 = unModifiedRecords.withColumn("uuid", functions.lit(uuid));
dataToInsert1.write().mode("append").format("parquet").insertInto(table + "_compacted");
dataToInsert2.write().mode("append").format("parquet").insertInto(table + "_compacted");
}
private static String getHdfsFileLocation(String table) {
Dataset<Row> tableDescription = sqlContext.sql("describe formatted " + table + "_compacted");
List<Row> rows = tableDescription.collectAsList();
String location = null;
for (Row r : rows) {
if (r.get(0).equals("Location")) {
location = r.getString(1);
break;
}
}
return location;
}
private static String frameQuery(String delimiter, Map mapper) {
StringBuilder modifiedQuery = new StringBuilder();
int i = 1;
for (Object key : mapper.keySet()) {
modifiedQuery.append(key + "=");
modifiedQuery.append(mapper.get(key));
if (mapper.size() > i)
modifiedQuery.append(delimiter);
i++;
}
return modifiedQuery.toString();
}
private static String framePath(String location, String framedpartition, String uuid) {
StringBuilder loc = new StringBuilder(location);
loc.append("/");
if (StringUtils.isNotEmpty(framedpartition)) {
loc.append(framedpartition);
loc.append("/");
}
loc.append("uuid=");
loc.append(uuid);
logger.info(loc.toString());
return loc.toString();
}
public static Seq<String> getColumnSeq(List<String> joinColumns) {
List<String> cols = new ArrayList<>(joinColumns.size());
for (int i = 0; i < joinColumns.size(); i++) {
cols.add(joinColumns.get(i).toLowerCase());
}
return JavaConverters.asScalaBufferConverter(cols).asScala().readOnly();
}
private static String getUUID() {
StringBuilder uri = new StringBuilder();
Random rand = new Random();
int randNum = rand.nextInt(200);
String uuid = DateTimeFormatter.ofPattern("yyyyMMddHHmmSSS").format(LocalDateTime.now()).toString()
+ (String.valueOf(randNum));
return uuid;
}
private static List<Map<Object, Object>> getPartitionsToCompact(Dataset<Row> filteredRecords,
List<String> partitions) {
Column[] columns = new Column[partitions.size()];
int index = 0;
for (String c : partitions) {
columns[index] = new Column(c);
index++;
}
Dataset<Row> partitionsToCompact = filteredRecords.select(columns)
.distinct(); /**
* TOD : add filter condition for selecting
* known paritions
*/
JavaRDD<Map<Object, Object>> querywithPartitions = partitionsToCompact.toJavaRDD().map(row -> {
return convertRowToMap(row);
});
return querywithPartitions.collect();
}
private static Map<Object, Object> convertRowToMap(Row row) {
StructField[] fields = row.schema().fields();
List<StructField> structFields = Arrays.asList(fields);
Map<Object, Object> a = structFields.stream()
.collect(Collectors.toMap(e -> ((StructField) e).name(), e -> row.getAs(e.name())));
return a;
}
private static Seq<String> getPrimaryKeyColumns() {
ArrayList<String> primaryKeyColumns = new ArrayList<String>();
Seq<String> joinColumnSeq = getColumnSeq(primaryKeyColumns);
return joinColumnSeq;
}
/*
* public static void initSpark(String jobname) { sparkConf = new
* SparkConf().setAppName(jobname); sparkConf.setMaster("local[3]");
* sparkConf.set("spark.driver.allowMultipleContexts", "true"); sc = new
* JavaSparkContext(); sqlContext = new SQLContext(sc); }
*/
public static HashMap<Object, Object> getParitionColumns() {
HashMap<Object, Object> paritionColumns = new HashMap<Object, Object>();
paritionColumns.put((Object) "staging.dummy_table", "trade_date,dwh_business_date,region_cd");
return paritionColumns;
}
public static void initialize(String table) {
// initSpark("Hive Table Compaction -" + table);
partitionColumns = getParitionColumns();
}
}
Usage:
String table = "staging.dummy_table";
HiveCompaction.initialize(table);
Dataset<Row> dataToCompact = sparkSession.sql("select * from staging.dummy_table");
HiveCompaction.compact(table, dataToCompact);
sparkSession.sql("select * from staging.dummy_table_compacted").show();
System.out.println("Compaction successful");

Integer Columns in ScalaFX TableView

I am new to ScalaFX. I am trying to adapt a basic TableView example, to include Integer columns.
So far, I have come up with the following code:
class Person(firstName_ : String, age_ : Int) {
val name = new StringProperty(this, "Name", firstName_)
val age = new IntegerProperty(this, "Age", age_)
}
object model{
val dataSource = new ObservableBuffer[Person]()
dataSource += new Person("Moe", 45)
dataSource += new Person("Larry", 43)
dataSource += new Person("Curly", 41)
dataSource += new Person("Shemp", 39)
dataSource += new Person("Joe", 37)
}
object view{
val nameCol = new TableColumn[Person, String]{
text = "Name"
cellValueFactory = {_.value.name}
}
val ageCol = new TableColumn[Person, Int]{
text = "Age"
cellValueFactory = {_.value.age}
}
}
object TestTableView extends JFXApp {
stage = new PrimaryStage {
title = "ScalaFx Test"
width = 800; height = 500
scene = new Scene {
content = new TableView[Person](model.dataSource){
columns += view.nameCol
columns += view.ageCol
}
}
}
}
The problem is that, while the nameCol works well, the ageCol doesn't even compile.
In the line cellValueFactory = {_.value.age}, I get a type mismatch error. It is expecting a ObservableValue[Int,Int] but getting an IntegerProperty.
I am using ScalaFX 1.0 M2, compiled for Scala 2.10.
Change IntegerProperty to ScalaFX ObjectProperty[Int], simply:
val age = ObjectProperty(this, "Age", age_)
The rest can stay the same.
so try...
TableColumn<Person, String> firstNameCol = new TableColumn<>("First Name");
or table action
TableColumn<Person, Boolean> actionCol = new TableColumn<>("Action");
actionCol.setSortable(false);
actionCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Person, Boolean>, ObservableValue<Boolean>>() {
#Override public ObservableValue<Boolean> call(TableColumn.CellDataFeatures<Person, Boolean> features) {
return new SimpleBooleanProperty(features.getValue() != null);
}
});

With OrmLite, is there a way to automatically update table schema when my POCO is modified?

Can OrmLite recognize differences between my POCO and my schema and automatically add (or remove) columns as necessary to force the schema to remain in sync with my POCO?
If this ability doesn't exist, is there way for me to query the db for table schema so that I may manually perform the syncing? I found this, but I'm using the version of OrmLite that installs with ServiceStack and for the life of me, I cannot find a namespace that has the TableInfo classes.
I created an extension method to automatically add missing columns to my tables. Been working great so far. Caveat: the code for getting the column names is SQL Server specific.
namespace System.Data
{
public static class IDbConnectionExtensions
{
private static List<string> GetColumnNames(IDbConnection db, string tableName)
{
var columns = new List<string>();
using (var cmd = db.CreateCommand())
{
cmd.CommandText = "exec sp_columns " + tableName;
var reader = cmd.ExecuteReader();
while (reader.Read())
{
var ordinal = reader.GetOrdinal("COLUMN_NAME");
columns.Add(reader.GetString(ordinal));
}
reader.Close();
}
return columns;
}
public static void AlterTable<T>(this IDbConnection db) where T : new()
{
var model = ModelDefinition<T>.Definition;
// just create the table if it doesn't already exist
if (db.TableExists(model.ModelName) == false)
{
db.CreateTable<T>(overwrite: false);
return;
}
// find each of the missing fields
var columns = GetColumnNames(db, model.ModelName);
var missing = ModelDefinition<T>.Definition.FieldDefinitions
.Where(field => columns.Contains(field.FieldName) == false)
.ToList();
// add a new column for each missing field
foreach (var field in missing)
{
var alterSql = string.Format("ALTER TABLE {0} ADD {1} {2}",
model.ModelName,
field.FieldName,
db.GetDialectProvider().GetColumnTypeDefinition(field.FieldType)
);
Console.WriteLine(alterSql);
db.ExecuteSql(alterSql);
}
}
}
}
No there is no current support for Auto Migration of RDBMS Schema's vs POCOs in ServiceStack's OrmLite.
There are currently a few threads being discussed in OrmLite's issues that are exploring the different ways to add this.
Here is a slightly modified version of code from cornelha to work with PostgreSQL. Removed this fragment
//private static List<string> GetColumnNames(object poco)
//{
// var list = new List<string>();
// foreach (var prop in poco.GetType().GetProperties())
// {
// list.Add(prop.Name);
// }
// return list;
//}
and used IOrmLiteDialectProvider.NamingStrategy.GetTableName and IOrmLiteDialectProvider.NamingStrategy.GetColumnName methods to convert table and column names from PascalNotation to this_kind_of_notation used by OrmLite when creating tables in PostgreSQL.
public static class IDbConnectionExtensions
{
private static List<string> GetColumnNames(IDbConnection db, string tableName, IOrmLiteDialectProvider provider)
{
var columns = new List<string>();
using (var cmd = db.CreateCommand())
{
cmd.CommandText = getCommandText(tableName, provider);
var tbl = new DataTable();
tbl.Load(cmd.ExecuteReader());
for (int i = 0; i < tbl.Columns.Count; i++)
{
columns.Add(tbl.Columns[i].ColumnName);
}
}
return columns;
}
private static string getCommandText(string tableName, IOrmLiteDialectProvider provider)
{
if (provider == PostgreSqlDialect.Provider)
return string.Format("select * from {0} limit 1", tableName);
else return string.Format("select top 1 * from {0}", tableName);
}
public static void AlterTable<T>(this IDbConnection db, IOrmLiteDialectProvider provider) where T : new()
{
var model = ModelDefinition<T>.Definition;
var table = new T();
var namingStrategy = provider.NamingStrategy;
// just create the table if it doesn't already exist
var tableName = namingStrategy.GetTableName(model.ModelName);
if (db.TableExists(tableName) == false)
{
db.CreateTable<T>(overwrite: false);
return;
}
// find each of the missing fields
var columns = GetColumnNames(db, model.ModelName, provider);
var missing = ModelDefinition<T>.Definition.FieldDefinitions
.Where(field => columns.Contains(namingStrategy.GetColumnName(field.FieldName)) == false)
.ToList();
// add a new column for each missing field
foreach (var field in missing)
{
var columnName = namingStrategy.GetColumnName(field.FieldName);
var alterSql = string.Format("ALTER TABLE {0} ADD COLUMN {1} {2}",
tableName,
columnName,
db.GetDialectProvider().GetColumnTypeDefinition(field.FieldType)
);
Console.WriteLine(alterSql);
db.ExecuteSql(alterSql);
}
}
}
I implemented an UpdateTable function. The basic idea is:
Rename current table on database.
Let OrmLite create the new schema.
Copy the relevant data from the old table to the new.
Drop the old table.
Github Repo: https://github.com/peheje/Extending-NServiceKit.OrmLite
Condensed code:
public interface ISqlProvider
{
string RenameTableSql(string currentName, string newName);
string GetColumnNamesSql(string tableName);
string InsertIntoSql(string intoTableName, string fromTableName, string commaSeparatedColumns);
string DropTableSql(string tableName);
}
public static void UpdateTable<T>(IDbConnection connection, ISqlProvider sqlProvider) where T : new()
{
connection.CreateTableIfNotExists<T>();
var model = ModelDefinition<T>.Definition;
string tableName = model.Name;
string tableNameTmp = tableName + "Tmp";
string renameTableSql = sqlProvider.RenameTableSql(tableName, tableNameTmp);
connection.ExecuteNonQuery(renameTableSql);
connection.CreateTable<T>();
string getModelColumnsSql = sqlProvider.GetColumnNamesSql(tableName);
var modelColumns = connection.SqlList<string>(getModelColumnsSql);
string getDbColumnsSql = sqlProvider.GetColumnNamesSql(tableNameTmp);
var dbColumns = connection.SqlList<string>(getDbColumnsSql);
List<string> activeFields = dbColumns.Where(dbColumn => modelColumns.Contains(dbColumn)).ToList();
string activeFieldsCommaSep = ListToCommaSeparatedString(activeFields);
string insertIntoSql = sqlProvider.InsertIntoSql(tableName, tableNameTmp, activeFieldsCommaSep);
connection.ExecuteSql(insertIntoSql);
string dropTableSql = sqlProvider.DropTableSql(tableNameTmp);
//connection.ExecuteSql(dropTableSql); //maybe you want to clean up yourself, else uncomment
}
private static String ListToCommaSeparatedString(List<String> source)
{
var sb = new StringBuilder();
for (int i = 0; i < source.Count; i++)
{
sb.Append(source[i]);
if (i < source.Count - 1)
{
sb.Append(", ");
}
}
return sb.ToString();
}
}
MySql implementation:
public class MySqlProvider : ISqlProvider
{
public string RenameTableSql(string currentName, string newName)
{
return "RENAME TABLE `" + currentName + "` TO `" + newName + "`;";
}
public string GetColumnNamesSql(string tableName)
{
return "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '" + tableName + "';";
}
public string InsertIntoSql(string intoTableName, string fromTableName, string commaSeparatedColumns)
{
return "INSERT INTO `" + intoTableName + "` (" + commaSeparatedColumns + ") SELECT " + commaSeparatedColumns + " FROM `" + fromTableName + "`;";
}
public string DropTableSql(string tableName)
{
return "DROP TABLE `" + tableName + "`;";
}
}
Usage:
using (var db = dbFactory.OpenDbConnection())
{
DbUpdate.UpdateTable<SimpleData>(db, new MySqlProvider());
}
Haven't tested with FKs. Can't handle renaming properties.
I needed to implement something similiar and found the post by Scott very helpful. I decided to make a small change which will make it much more agnostic. Since I only use Sqlite and MSSQL, I made the getCommand method very simple, but can be extended. I used a simple datatable to get the columns. This solution works perfectly for my requirements.
public static class IDbConnectionExtensions
{
private static List<string> GetColumnNames(IDbConnection db, string tableName,IOrmLiteDialectProvider provider)
{
var columns = new List<string>();
using (var cmd = db.CreateCommand())
{
cmd.CommandText = getCommandText(tableName, provider);
var tbl = new DataTable();
tbl.Load(cmd.ExecuteReader());
for (int i = 0; i < tbl.Columns.Count; i++)
{
columns.Add(tbl.Columns[i].ColumnName);
}
}
return columns;
}
private static string getCommandText(string tableName, IOrmLiteDialectProvider provider)
{
if(provider == SqliteDialect.Provider)
return string.Format("select * from {0} limit 1", tableName);
else return string.Format("select top 1 * from {0}", tableName);
}
private static List<string> GetColumnNames(object poco)
{
var list = new List<string>();
foreach (var prop in poco.GetType().GetProperties())
{
list.Add(prop.Name);
}
return list;
}
public static void AlterTable<T>(this IDbConnection db, IOrmLiteDialectProvider provider) where T : new()
{
var model = ModelDefinition<T>.Definition;
var table = new T();
// just create the table if it doesn't already exist
if (db.TableExists(model.ModelName) == false)
{
db.CreateTable<T>(overwrite: false);
return;
}
// find each of the missing fields
var columns = GetColumnNames(db, model.ModelName,provider);
var missing = ModelDefinition<T>.Definition.FieldDefinitions
.Where(field => columns.Contains(field.FieldName) == false)
.ToList();
// add a new column for each missing field
foreach (var field in missing)
{
var alterSql = string.Format("ALTER TABLE {0} ADD {1} {2}",
model.ModelName,
field.FieldName,
db.GetDialectProvider().GetColumnTypeDefinition(field.FieldType)
);
Console.WriteLine(alterSql);
db.ExecuteSql(alterSql);
}
}
}
So I took user44 answer, and modified the AlterTable method to make it a bit more efficient.
Instead of looping and running one SQL query per field/column, I merge it into one with some simple text parsing (MySQL commands!).
public static void AlterTable<T>(this IDbConnection db, IOrmLiteDialectProvider provider) where T : new()
{
var model = ModelDefinition<T>.Definition;
var table = new T();
var namingStrategy = provider.NamingStrategy;
// just create the table if it doesn't already exist
var tableName = namingStrategy.GetTableName(model.ModelName);
if (db.TableExists(tableName) == false)
{
db.CreateTable<T>(overwrite: false);
return;
}
// find each of the missing fields
var columns = GetColumnNames(db, model.ModelName, provider);
var missing = ModelDefinition<T>.Definition.FieldDefinitions
.Where(field => columns.Contains(namingStrategy.GetColumnName(field.FieldName)) == false)
.ToList();
string alterSql = "";
string addSql = "";
// add a new column for each missing field
foreach (var field in missing)
{
var alt = db.GetDialectProvider().ToAddColumnStatement(typeof(T), field); // Should be made more efficient, one query for all changes instead of many
int index = alt.IndexOf("ADD ");
alterSql = alt.Substring(0, index);
addSql += alt.Substring(alt.IndexOf("ADD COLUMN")).Replace(";", "") + ", ";
}
if (addSql.Length > 2)
addSql = addSql.Substring(0, addSql.Length - 2);
string fullSql = alterSql + addSql;
Console.WriteLine(fullSql);
db.ExecuteSql(fullSql);
}

Resources