Kotlin Room Database: How to save result from sum query? [duplicate] - database

This question already has answers here:
Android Room - simple select query - Cannot access database on the main thread
(23 answers)
Closed 8 months ago.
I´m new to Kotlin and trying to learn it by programming an app for work time recording.
I´ve created a room database which works fine when inserting data. In the next step I would like to retrieve the sum of a column and store this value in a variable. And this is the point where I get stuck. Here are the affected snippets of my code.
data class sumPojo(var sumOvertime: Double)
#Dao
interface OvertimeDao {
#Query(value = "SELECT SUM(overtime) as sumOvertime FROM TableOvertime")
fun getSumOvertime(): sumPojo
}
class OvertimeRepository(private val overtimeDao: OvertimeDao) {
val getSumOvertime: sumPojo = overtimeDao.getSumOvertime()
}
class OvertimeViewModel(application: Application): AndroidViewModel(application) {
private val repository : OvertimeRepository
val getSumOvertime : sumPojo
init {
val overtimeDao = OvertimeDatabase.getDatabase(application).overtimeDao()
repository = OvertimeRepository(overtimeDao)
getSumOvertime = repository.getSumOvertime
}
}
class inputWorktimeFragment : Fragment() {
private lateinit var mOvertimeViewModel : OvertimeViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_input_worktime, container, false)
mOvertimeViewModel = ViewModelProvider(this)[OvertimeViewModel::class.java]
val sumOvertime: sumPojo = mOvertimeViewModel.getSumOvertime
return view
}
}
Following the error message which I receive.
2022-07-06 16:24:03.067 7338-7338/com.example.workingtimerecorder E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.workingtimerecorder, PID: 7338
java.lang.RuntimeException: Cannot create an instance of class com.example.workingtimerecorder.data.OvertimeViewModel
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:320)
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:278)
at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.kt:128)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:187)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:153)
at com.example.workingtimerecorder.fragments.inputWorktimeFragment.onCreateView(InputWorktimeFragment.kt:55)
at androidx.fragment.app.Fragment.performCreateView(Fragment.java:3104)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:524)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:261)
at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:113)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1424)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2968)
at androidx.fragment.app.FragmentManager.dispatchViewCreated(FragmentManager.java:2879)
at androidx.fragment.app.Fragment.performViewCreated(Fragment.java:3129)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:552)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:261)
at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:113)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1424)
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2968)
at androidx.fragment.app.FragmentManager.dispatchActivityCreated(FragmentManager.java:2886)
at androidx.fragment.app.FragmentController.dispatchActivityCreated(FragmentController.java:263)
at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:351)
at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:246)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1455)
at android.app.Activity.performStart(Activity.java:8076)
at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3660)
at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:221)
at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:201)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:173)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2210)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7839)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Constructor.newInstance0(Native Method)
at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:312)
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:278) 
at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.kt:128) 
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:187) 
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:153) 
at com.example.workingtimerecorder.fragments.inputWorktimeFragment.onCreateView(InputWorktimeFragment.kt:55) 
at androidx.fragment.app.Fragment.performCreateView(Fragment.java:3104) 
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:524) 
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:261) 
at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:113) 
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1424) 
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2968) 
at androidx.fragment.app.FragmentManager.dispatchViewCreated(FragmentManager.java:2879) 
at androidx.fragment.app.Fragment.performViewCreated(Fragment.java:3129) 
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:552) 
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:261) 
at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:113) 
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1424) 
at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2968) 
at androidx.fragment.app.FragmentManager.dispatchActivityCreated(FragmentManager.java:2886) 
at androidx.fragment.app.FragmentController.dispatchActivityCreated(FragmentController.java:263) 
at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:351) 
at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:246) 
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1455) 
at android.app.Activity.performStart(Activity.java:8076) 
at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3660) 
at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:221) 
at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:201) 
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:173) 
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2210) 
at android.os.Handler.dispatchMessage(Handler.java:106) 
at android.os.Looper.loopOnce(Looper.java:201) 
at android.os.Looper.loop(Looper.java:288) 
at android.app.ActivityThread.main(ActivityThread.java:7839) 
at java.lang.reflect.Method.invoke(Native Method) 
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003) 
Caused by: java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
at androidx.room.RoomDatabase.assertNotMainThread(RoomDatabase.java:469)
at androidx.room.RoomDatabase.query(RoomDatabase.java:525)
at androidx.room.util.DBUtil.query(DBUtil.java:86)
2022-07-06 16:24:03.068 7338-7338/com.example.workingtimerecorder E/AndroidRuntime: at com.example.workingtimerecorder.data.OvertimeDao_Impl.getSumOvertime(OvertimeDao_Impl.java:124)
at com.example.workingtimerecorder.data.OvertimeRepository.<init>(OvertimeRepository.kt:16)
at com.example.workingtimerecorder.data.OvertimeViewModel.<init>(OvertimeViewModel.kt:24)
... 40 more
I would appreciate your help or any hints which could lead me into the rigth direction. I`ve read about several similar issues here on Stackoverflow but none of them could help me.

Caused by: java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
I'd try returning either flow or live data and using view model scope to start a co routine.
in the dao
//change
fun getSumOvertime(): sumPojo
to
fun getSumOvertime(): LiveData<sumPojo>
//or
suspend fun getSumOvertime(): Flow<sumPojo>
in the repo
//livedata
fun getSumPojo():LiveData<sumPojo>{return yourdb.yourdao.getSumovertime}
//flow
suspend fun getSumPojo():Flow<sumPojo>{return yourdb.yourdao.getSumovertime}
in the view model id gain access to the values like such
// create private and public accessors
private var _sumPojo = MutableLiveData<SumPojo> //use Int as mutable type if you do not have an object to map to
val sumPojo: LiveData<SumPojo> // again if you do not have an object for sumPojo use Int as live data type
get() = _sumPojo
private fun refreshSumPojo()=viewModelScope.launch{return yourRepo.getSumPojo()}
init {_sumPojo.value = refreshSumPojo}
in the view I would use databinding and bind the text view to the public accessor
in your view.xml file
<TextView
android:text="#{viewModel.sumPojo.toString()}"
" />

Related

Room Data Base Create Instance

I want to Create An Instance Of Room Data base in Composable
But
val db = Room.databaseBuilder(applicationContext, UserDatabase::class.java,"users.db").build()
is not working here not getting applicationContext
How to create an instance of context in composable
Have you tried getting the context with : val context = LocalContext.current and then adding this to get your applicationContext?
Like this: context.applicationContext or using simply val db = Room.databaseBuilder(context, UserDatabase::class.java,"users.db").build()
Room (and the underlying SQliteOpenHelper) only need the context to open the database (or more correctly to instantiate the underlying SQLiteOpenHelper).
Room/Android SQLiteOpenHelper uses the context to ascertain the Application's standard (recommended) location (data/data/<the_package_name>/databases). e.g. in the following demo (via Device Explorer):-
The database, as it is still open includes 3 files (the -wal and -shm are the Write Ahead Logging files that will at sometime be committed/written to the actual database (SQLite handles that)).
so roughly speaking Room only needs to have the context so that it can ascertain /data/data/a.a.so75008030kotlinroomgetinstancewithoutcontext/databases/testit.db (in the case of the demo).
So if you cannot use the applicationContext method then you can circumvent the need to provide the context, if using a singleton approach AND if after instantiating the singleton.
Perhaps consider this demo:-
First some pretty basic DB Stuff (table (#Entity annotated class), DAO functions and #Database annotated abstract class WITH singleton approach). BUT with some additional functions for accessing the instance without the context.
#Entity
data class TestIt(
#PrimaryKey
val testIt_id: Long?=null,
val testIt_name: String
)
#Dao
interface DAOs {
#Insert(onConflict = OnConflictStrategy.IGNORE)
fun insert(testIt: TestIt): Long
#Query("SELECT * FROM testit")
fun getAllTestItRows(): List<TestIt>
}
#Database(entities = [TestIt::class], exportSchema = false, version = 1)
abstract class TestItDatabase: RoomDatabase() {
abstract fun getDAOs(): DAOs
companion object {
private var instance: TestItDatabase?=null
/* Extra/not typical for without a context (if wanted)*/
fun isInstanceWithoutContextAvailable() : Boolean {
return instance != null
}
/******************************************************/
/* Extra/not typical for without a context */
/******************************************************/
fun getInstanceWithoutContext(): TestItDatabase? {
if (instance != null) {
return instance as TestItDatabase
}
return null
}
/* Typically the only function*/
fun getInstance(context: Context): TestItDatabase {
if (instance==null) {
instance = Room.databaseBuilder(context,TestItDatabase::class.java,"testit.db")
.allowMainThreadQueries() /* for convenience/brevity of demo */
.build()
}
return instance as TestItDatabase
}
}
}
And to demonstrate (within an activity for brevity) :-
class MainActivity : AppCompatActivity() {
lateinit var roomInstance: TestItDatabase
lateinit var dao: DAOs
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
roomInstance = TestItDatabase.getInstance(this) /* MUST be used before withoutContext functions but could be elsewhere shown here for brevity */
dao = roomInstance.getDAOs()
//dao.insert(TestIt(testIt_name = "New001")) /* Removed to test actually doing the database open with the without context */
logDataWithoutContext()
addRowWithoutContext()
addRowWithApplicationContext()
logDataWithoutContext()
}
private fun logDataWithoutContext() {
Log.d("${TAG}_LDWC","Room DB Instantiated = ${TestItDatabase.isInstanceWithoutContextAvailable()}")
for (t in TestItDatabase.getInstanceWithoutContext()!!.getDAOs().getAllTestItRows()) {
Log.d("${TAG}_LDWC_DATA","TestIt Name is ${t.testIt_name} ID is ${t.testIt_id}")
}
}
private fun addRowWithoutContext() {
Log.d("${TAG}_LDWC","Room DB Instantiated = ${TestItDatabase.isInstanceWithoutContextAvailable()}")
if (TestItDatabase.getInstanceWithoutContext()!!.getDAOs()
.insert(TestIt(System.currentTimeMillis(),"NEW AS PER ID (the time to millis) WITHOUT CONTEXT")) > 0) {
Log.d("${TAG}_ARWC_OK","Row successfully inserted.")
} else {
Log.d("${TAG}_ARWC_OUCH","Row was not successfully inserted (duplicate ID)")
}
}
private fun addRowWithApplicationContext() {
TestItDatabase.getInstance(applicationContext).getDAOs().insert(TestIt(System.currentTimeMillis() / 1000,"NEW AS PER ID (the time to seconds) WITH CONTEXT"))
}
}
The result output to the log showing that the database access, either way, worked:-
2023-01-05 12:45:39.020 D/DBINFO_LDWC: Room DB Instantiated = true
2023-01-05 12:45:39.074 D/DBINFO_LDWC: Room DB Instantiated = true
2023-01-05 12:45:39.077 D/DBINFO_ARWC_OK: Row successfully inserted.
2023-01-05 12:45:39.096 D/DBINFO_LDWC: Room DB Instantiated = true
2023-01-05 12:45:39.098 D/DBINFO_LDWC_DATA: TestIt Name is NEW AS PER ID (the time to seconds) WITH CONTEXT ID is 1672883139
2023-01-05 12:45:39.098 D/DBINFO_LDWC_DATA: TestIt Name is NEW AS PER ID (the time to millis) WITHOUT CONTEXT ID is 1672883139075
note that the shorter id was the last added but appears first due to it being selected first as it appears earlier in the index that the SQlite Query Optimiser would have used (aka the Primary Key).
basically the same date time second wise but the first insert included milliseconds whilst the insert via AddRowWithApplicationContext drops the milliseconds.

Kotlin room database boolean

Im trying to create an app where a user can has a list of goals of steps to reach they create and then choose one of them to be active and to follow. The database works when I was just using the goal id, name, and steps but now I realised I need to insert another column defining when a goal is active so Im trying to insert that, however I don't know how I should handle the boolean especially in the repository and viewModel. I'd appreciate any help. Thanks in advance
here's my code
interface Dao {
#Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insert(goal: Goal)
#Update
suspend fun updateGoal(goal: Goal)
#Query("SELECT * FROM user_goal_table order by goalId")
fun getAll(): LiveData<List<Goal>>
#Query("SELECT * FROM user_goal_table WHERE goalId = :key")
suspend fun getGoal(key: Int): Goal
#Delete
suspend fun delete(goal: Goal)
#Query("SELECT * FROM user_goal_table WHERE goal_is_active = 1 order by goalId")
suspend fun makeGoalActive(key: Int): Goal
class Repository (private val dao : Dao){
val allGoals: LiveData<List<Goal>> = Dao.getAll()
suspend fun insert(goal: Goal){
dao.insert(goal)
}
suspend fun update(goal: Goal){
dao.update(goal)
}
suspend fun delete(goal: Goal){
dao.delete(goal)
}
suspend fun active(goal: Goal, int: Int){
dao.makeGoalActive(int)
}
class ViewModel (application: Application) : AndroidViewModel(application) {
val allGoals: LiveData<List<Goal>>
private val repository: Repository
init{
val dao = GoalDatabase.getInstance(application).getGoalDao()
repo = Repository(dao)
allGoals = repository.allGoals
}
fun insert(goal: Goal) = viewModelScope.launch(Dispatchers.IO){
repository.insert(goal)
}
fun update(goal: Goal) = viewModelScope.launch(Dispatchers.IO){
repository.update(goal)
}
fun delete(goal: Goal) = viewModelScope.launch(Dispatchers.IO){
repository.delete(goal)
}
Just add a new property like isActive: Boolean to your Goal class and then use #Update annotation in Room (that you've already implemented in updateGoal(goal: Goal) method of your Dao) or UPDATE command itself in SQLite to update the row you want to change its isActive state. For using SQLite do something like below:
#Query("UPDATE user_goal_table SET isActive = 1 WHERE goalId = :goalId")
suspend fun makeGoalActive(goalId: Int)
For boolean properties, use 1 for true and 0 for false in SQLite commands.
In Repository, this method is enough:
suspend fun active(goalId: Int) {
dao.makeGoalActive(int)
}
And in the ViewModel:
fun insert(goal: Goal) = viewModelScope.launch {
repository.insert(goal)
}
Btw, you don't need to determine IO dispatcher for Room methods, Room uses its own dispatcher to run queries.

Flink JDBC Sink part 2

I have posted a question few days back- Flink Jdbc sink
Now, I am trying to use the sink provided by flink.
I have written the code and it worked as well. But nothing got saved in DB and no exceptions were there. Using previous sink my code was not finishing(that should happen ideally as its a streaming app) but after the following code I am getting no error and the nothing is getting saved to DB.
public class CompetitorPipeline implements Pipeline {
private final StreamExecutionEnvironment streamEnv;
private final ParameterTool parameter;
private static final Logger LOG = LoggerFactory.getLogger(CompetitorPipeline.class);
public CompetitorPipeline(StreamExecutionEnvironment streamEnv, ParameterTool parameter) {
this.streamEnv = streamEnv;
this.parameter = parameter;
}
#Override
public KeyedStream<CompetitorConfig, String> start(ParameterTool parameter) throws Exception {
CompetitorConfigChanges competitorConfigChanges = new CompetitorConfigChanges();
KeyedStream<CompetitorConfig, String> competitorChangesStream = competitorConfigChanges.run(streamEnv, parameter);
//Add to JBDC Sink
competitorChangesStream.addSink(JdbcSink.sink(
"insert into competitor_config_universe(marketplace_id,merchant_id, competitor_name, comp_gl_product_group_desc," +
"category_code, competitor_type, namespace, qualifier, matching_type," +
"zip_region, zip_code, competitor_state, version_time, compConfigTombstoned, last_updated) values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
(ps, t) -> {
ps.setInt(1, t.getMarketplaceId());
ps.setLong(2, t.getMerchantId());
ps.setString(3, t.getCompetitorName());
ps.setString(4, t.getCompGlProductGroupDesc());
ps.setString(5, t.getCategoryCode());
ps.setString(6, t.getCompetitorType());
ps.setString(7, t.getNamespace());
ps.setString(8, t.getQualifier());
ps.setString(9, t.getMatchingType());
ps.setString(10, t.getZipRegion());
ps.setString(11, t.getZipCode());
ps.setString(12, t.getCompetitorState());
ps.setTimestamp(13, Timestamp.valueOf(t.getVersionTime()));
ps.setBoolean(14, t.isCompConfigTombstoned());
ps.setTimestamp(15, new Timestamp(System.currentTimeMillis()));
System.out.println("sql"+ps);
},
new JdbcConnectionOptions.JdbcConnectionOptionsBuilder()
.withUrl("jdbc:mysql://127.0.0.1:3306/database")
.withDriverName("com.mysql.cj.jdbc.Driver")
.withUsername("xyz")
.withPassword("xyz#")
.build()));
return competitorChangesStream;
}
}
You need enable autocommit mode for jdbc Sink.
new JdbcConnectionOptions.JdbcConnectionOptionsBuilder()
.withUrl("jdbc:mysql://127.0.0.1:3306/database;autocommit=true")
It looks like SimpleBatchStatementExecutor only works in auto-commit mode. And if you need to commit and rollback batches, then you have to write your own ** JdbcBatchStatementExecutor **
Have you tried to include the JdbcExecutionOptions ?
dataStream.addSink(JdbcSink.sink(
sql_statement,
(statement, value) -> {
/* Prepared Statement */
},
JdbcExecutionOptions.builder()
.withBatchSize(5000)
.withBatchIntervalMs(200)
.withMaxRetries(2)
.build(),
new JdbcConnectionOptions.JdbcConnectionOptionsBuilder()
.withUrl("jdbc:mysql://127.0.0.1:3306/database")
.withDriverName("com.mysql.cj.jdbc.Driver")
.withUsername("xyz")
.withPassword("xyz#")
.build()));

What's the relationship between key and Window instance in KeyedStream#timeWindow#process

For KeyedStream#timeWindow#process, I am wonderring whether one window instance will only contain the same key, and different keys will use different window instances.
From the output of the following application, i see that one window instance will only contain the same key, and different keys will use different windows.
But I want to ask and confirm, thanks!
import org.apache.flink.streaming.api.functions.source.{RichParallelSourceFunction, SourceFunction}
import scala.util.Random
class KeyByAndWindowAndProcessTestSource extends RichParallelSourceFunction[Int] {
override def run(ctx: SourceFunction.SourceContext[Int]): Unit = {
while (true) {
val i = new Random().nextInt(30)
ctx.collect(i)
ctx.collect(i)
ctx.collect(i)
Thread.sleep(1000)
}
}
override def cancel(): Unit = {
}
}
The applications is:
import org.apache.flink.streaming.api.TimeCharacteristic
import org.apache.flink.streaming.api.scala.function.ProcessWindowFunction
import org.apache.flink.streaming.api.scala.{DataStream, StreamExecutionEnvironment}
import org.apache.flink.streaming.api.windowing.time.Time
import org.apache.flink.streaming.api.windowing.windows.TimeWindow
import org.apache.flink.util.Collector
import org.apache.flink.api.scala._
object KeyByAndWindowTest {
def main(args: Array[String]): Unit = {
val env = StreamExecutionEnvironment.getExecutionEnvironment
env.setParallelism(1)
env.getCheckpointConfig.setCheckpointInterval(10 * 1000)
env.setStreamTimeCharacteristic(TimeCharacteristic.ProcessingTime)
val ds: DataStream[Int] = env.addSource(new KeyByAndWindowAndProcessTestSource)
val ds2 = ds.keyBy(i => i).timeWindow(Time.seconds(4)).process(new MyProcessFunction())
ds2.print()
env.execute()
}
}
class MyProcessFunction extends ProcessWindowFunction[Int, String, Int, TimeWindow] {
override def process(
key: Int,
ctx: Context,
vals: Iterable[Int],
out: Collector[String]): Unit = {
println(new java.util.Date())
println(s"key=${key}, vals = ${vals.mkString(",")}, hashCode=${System.identityHashCode(ctx.window)}")
}
}
The output is:
Sat Sep 14 13:08:24 CST 2019
key=26, vals = 26,26,26, hashCode=838523304
Sat Sep 14 13:08:24 CST 2019
key=28, vals = 28,28,28, hashCode=472721641
Sat Sep 14 13:08:24 CST 2019
key=18, vals = 18,18,18,18,18,18, hashCode=1668151956
Actually, with respect to ProcessingTimeWindow, a new window object is created for each element.
Here is the source code of TumblingProcessingTimeWindows#assignWindows:
public Collection<TimeWindow> assignWindows(Object element, long timestamp, WindowAssignerContext context) {
final long now = context.getCurrentProcessingTime();
long start = TimeWindow.getWindowStartWithOffset(now, offset, size);
return Collections.singletonList(new TimeWindow(start, start + size));
}
So System.identityHashCode will always return a unique hash code for different keys, and your test code does not prove anything.
Under the hood, elements are grouped by the key of elementKey + assignedWindow, so I think it's right to say "one window instance will only contain the same key, and different keys will use different window instances".
Original Answer:
I hope I get your question right...
ProcessWindowFunction#process will be invoked for each window and key once (or multiple times depending on the windows's trigger). Internally, window and key make up a composite partition key.
In terms of Java object instances, one instance of ProcessWindowFunction will deal with many keys. Specifically, there will be degree of parallelism many ProcessWindowFunctions.
Follow Up:
So I did not get it right :)
For every record, which is processed by the WindowOperator a new Window object is created, with the correct start/end time for the record.
This means that each invocation of ProcessWindowFunction#process will be passed a new Window object.
It is important to understand, that a Window in Flink is a very light object, which is just used as an additional part (the namespace) of the overall key. It does not hold any data and/or logic.
May I ask for the background of the question?

spring-data-mongodb bulkOps cant not serialize enum

I an enum field in my model,when I use find method to query some data with this field as condition, it return as wanted.
but, when I use bulkOps and excute an upsert operation,it tells me:
can't serialize class com.timanetworks.tpc.vehicle.alarm.domain.enums.FaultType
I am try to add name() method of enum to resolve this problem, and it's success! But I still can not understand why? does any one know?
my model is like this:
#Document(collection = "FaultSnapshot")
#TypeAlias("faultSnapshot")
public class FaultSnapshot extends BaseDocument {
private FaultType type;
private String vin;
private Integer faultLevel;
private Boolean isFault;
private Date time;
...setters and getters...
and FaultType is an enum:
public enum FaultType {
FAULT_EMS,
....
}
and this is find code:
Query query = new Query();
if (type != null) {
query.addCriteria(where("type").is(type));
}
Pageable pageable = new PageRequest(pageIndex - 1, pageSize);
Sort sort = new Sort(Sort.Direction.DESC, "time");
List<FaultHistory> histories = template.find(query.with(pageable).with(sort),
FaultHistory.class);
return new Page<>(count, histories);
this is bulkOps code:
public void upsertSnapshot(Collection<FaultSnapshot> snapshots) {
BulkOperations bulk = template.bulkOps(BulkOperations.BulkMode.UNORDERED, FaultSnapshot.class);
for (FaultSnapshot snapshot : snapshots) {
Query query = new Query();
query.addCriteria(where("vin").is(snapshot.getVin()));
query.addCriteria(where("type").is(snapshot.getType().name()));
Update update = new Update()
.set("isFault", snapshot.getFault())
.set("faultLevel", snapshot.getFaultLevel())
.set("time", snapshot.getTime())
.set("vin", snapshot.getVin())
.set("type", snapshot.getType().name());
bulk.upsert(query, update);
}
bulk.execute();
}
finally,this is the error stack:
Exception in thread "Thread-11" java.lang.IllegalArgumentException: can't serialize class com.timanetworks.tpc.vehicle.alarm.domain.enums.FaultType
at org.bson.BasicBSONEncoder._putObjectField(BasicBSONEncoder.java:299)
at org.bson.BasicBSONEncoder.putObject(BasicBSONEncoder.java:194)
at org.bson.BasicBSONEncoder._putObjectField(BasicBSONEncoder.java:255)
at org.bson.BasicBSONEncoder.putObject(BasicBSONEncoder.java:194)
at org.bson.BasicBSONEncoder.putObject(BasicBSONEncoder.java:136)
at com.mongodb.DefaultDBEncoder.writeObject(DefaultDBEncoder.java:36)
at com.mongodb.OutMessage.putObject(OutMessage.java:289)
at com.mongodb.OutMessage.writeUpdate(OutMessage.java:180)
at com.mongodb.OutMessage.update(OutMessage.java:60)
at com.mongodb.DBCollectionImpl$Run$1.executeWriteProtocol(DBCollectionImpl.java:908)
at com.mongodb.DBCollectionImpl$Run$RunExecutor.executeWriteProtocol(DBCollectionImpl.java:1025)
at com.mongodb.DBCollectionImpl$Run$RunExecutor.execute(DBCollectionImpl.java:1016)
at com.mongodb.DBCollectionImpl$Run.executeUpdates(DBCollectionImpl.java:917)
at com.mongodb.DBCollectionImpl$Run.execute(DBCollectionImpl.java:859)
at com.mongodb.DBCollectionImpl.executeBulkWriteOperation(DBCollectionImpl.java:169)
at com.mongodb.DBCollection.executeBulkWriteOperation(DBCollection.java:1904)
at com.mongodb.DBCollection.executeBulkWriteOperation(DBCollection.java:1899)
at com.mongodb.BulkWriteOperation.execute(BulkWriteOperation.java:116)
at org.springframework.data.mongodb.core.DefaultBulkOperations.execute(DefaultBulkOperations.java:276)
at com.timanetworks.tpc.vehicle.alarm.dao.FaultSnapshotRepositoryImpl.***upsertSnapshot***(FaultSnapshotRepositoryImpl.java:39)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
This is actually a bug (DATAMONGO-1678) in Spring Data MongoDB. It has already been fixed for 1.9.12, 1.10.5 and 2.0.0.RC1.
At the time of writing none of the mentioned versions has been released.

Resources