質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.46%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Android Studio

Android Studioは、 Google社によって開発された、 Androidのネイティブアプリケーション開発に特化した統合開発ツールです。

Q&A

解決済

1回答

492閲覧

android room に関するエラー

tetejiro

総合スコア15

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Android Studio

Android Studioは、 Google社によって開発された、 Androidのネイティブアプリケーション開発に特化した統合開発ツールです。

0グッド

0クリップ

投稿2024/02/17 11:32

編集2024/02/17 13:21

実現したいこと

Room を使用して、データベースに LocalDateTime 型を保存したいです。
Converter を使用するとの記載を見つけてやってみたのですが、
https://developer.android.com/training/data-storage/room/referencing-data?hl=ja
以下のエラーが出て、実現できませんでした。
何が間違えているのか詰まってしまっているため、アドバイスをいただけませんでしょうか?

また、もし知っていれば、スキーマを書き換えたときに
バージョンを上げるなどの処理をしなければならないと思うのですが、
その処理を実施しなくてもいい方法がもしあれば教えていただけませんでしょうか。
この辺のこと↓です。
https://qiita.com/kazuma_f/items/8c15e7087623e8f6706b

ご回答をいただけますととても嬉しいです。
どうぞよろしくお願いいたします。

発生している問題・エラーメッセージ

FATAL EXCEPTION: Thread-2 Process: com.example.RecordTime, PID: 13832 java.lang.IllegalArgumentException: Unexpected type converter com.example.RecordTime.Rooms.Converters@8399701. Annotate TypeConverter class with @ProvidedTypeConverter annotation or remove this converter from the builder. at androidx.room.RoomDatabase.init(RoomDatabase.kt:284) at androidx.room.RoomDatabase$Builder.build(RoomDatabase.kt:1352) at com.example.RecordTime.DateFragment$ConnectDB.run(DateFragment.java:90) at java.lang.Thread.run(Thread.java:1012)

該当のソースコード

DateFragment.java

1public class DateFragment extends Fragment { 2 3 Adapter adapter = new Adapter(); 4 5 TimeTableDao timeTableDao; 6 7 List<TimeTableEntity> timeTableEntities = new ArrayList<>(); 8 9 View view; 10 11 int year; 12 int month; 13 int date; 14 15 @Override 16 public void onCreate(@Nullable Bundle savedInstanceState) { 17 super.onCreate(savedInstanceState); 18 19 new Thread(new ConnectDB()).start(); 20 21 getParentFragmentManager().setFragmentResultListener("date", this, new FragmentResultListener() { 22 23 @Override 24 public void onFragmentResult(@NonNull String key, @NonNull Bundle bundle) { 25 year = bundle.getInt("year"); 26 month = bundle.getInt("month"); 27 date = bundle.getInt("date"); 28 29 // 〇年・〇月・〇日をセット 30 setDateText(); 31 } 32 }); 33 } 34 35 @Override 36 public View onCreateView(LayoutInflater inflater, ViewGroup container, 37 Bundle savedInstanceState) { 38 // Inflate the layout for this fragment 39 return inflater.inflate(R.layout.date_fragment, container, false); 40 } 41 42 @Override 43 public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 44 45 this.view = view; 46 47 new Thread(new SelectRec()).start(); 48 49 // RecyclerView をセット 50 RecyclerView recyclerView = view.findViewById(R.id.time_table_recycler_view); 51 recyclerView.setHasFixedSize(true); 52 recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); 53 recyclerView.setAdapter(adapter); 54 } 55 56 class ConnectDB implements Runnable { 57 @Override 58 public void run() { 59 AppDatabase database = Room.databaseBuilder(getActivity().getApplicationContext(), 60 AppDatabase.class, "TimeTable") 61 .addTypeConverter(new Converters()) 62 .build(); 63 timeTableDao = database.timeTableDao(); 64 } 65 } 66 67 class SelectRec implements Runnable { 68 @Override 69 public void run() { 70 timeTableDao.insert(new TimeTableEntity("time1")); 71 timeTableDao.insert(new TimeTableEntity("time2")); 72 timeTableEntities = timeTableDao.getAll(); 73 Log.d("================>", timeTableEntities.get(0).title); 74 } 75 } 76 77 public void setDateText() { 78 79 // 〇年をセット 80 TextView year_text = view.findViewById(R.id.selected_year); 81 year_text.setText(year + " 年"); 82 83 // 〇月をセット 84 TextView month_text = view.findViewById(R.id.selected_month); 85 month_text.setText(month + " 月"); 86 87 // 〇日をセット 88 TextView date_text = view.findViewById(R.id.selected_date); 89 date_text.setText(date + " 日"); 90 } 91 92 class Adapter extends RecyclerView.Adapter<Adapter.ViewHolder> { 93 94 public class ViewHolder extends RecyclerView.ViewHolder { 95 96 TextView textView; 97 98 public ViewHolder(@NonNull View itemView) { 99 super(itemView); 100 101 this.textView = itemView.findViewById(R.id.time_table_view_holder); 102 } 103 } 104 105 @NonNull 106 @Override 107 public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 108 View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.time_table_viewholder, parent, false); 109 return new ViewHolder(view); 110 } 111 112 @Override 113 public void onBindViewHolder(@NonNull ViewHolder holder, int position) { 114// holder.textView.setText(timeTableRoomEntities.get(position).title); 115 } 116 117 118 @Override 119 public int getItemCount() { 120 return 0; 121 } 122 } 123}

Converters.java

1@ProvidedTypeConverter 2public class Converters { 3 4 @TypeConverter 5 public static LocalDateTime toLocalDateTime(String val) { 6 return LocalDateTime.parse(val); 7 } 8 9 @TypeConverter 10 public static String fromLocalDateTime(LocalDateTime val) { 11 return val.toString(); 12 } 13}

AppDatabase.java

1@Database(entities = {TimeTableEntity.class}, version = 1) 2@TypeConverters(Converters.class) 3public abstract class AppDatabase extends RoomDatabase { 4 public abstract TimeTableDao timeTableDao(); 5}

TimeTableDao.java

1@Dao 2public interface TimeTableDao { 3 4 @Insert 5 void insert(TimeTableEntity timeTableEntity); 6 7 @Query("SELECT * FROM TimeTable") 8 List<TimeTableEntity> getAll(); 9}

TimeTableEntity.java

1@Entity(tableName = "TimeTable") 2public class TimeTableEntity { 3 4 @PrimaryKey 5 public int id; 6 7 @ColumnInfo(name = "dateTime") 8 public LocalDateTime dateTime; 9 10 @ColumnInfo(name = "title") 11 public String title; 12 13 public TimeTableEntity(String title) { 14 this.dateTime = LocalDateTime.parse(title); 15 } 16}

time_table_viewholder.xml

1<?xml version="1.0" encoding="utf-8"?> 2<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent"> 5 6 <TextView 7 android:id="@+id/time_table_view_holder" 8 android:layout_width="match_parent" 9 android:layout_height="wrap_content" 10 android:layout_weight="1" 11 android:ems="10" 12 android:text="@string/time_view_holder" /> 13 14</LinearLayout>

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

jimbe

2024/02/17 13:03

リンクが開けないです。 ![] の中に URL を書いてその後ろの () 内には書いてないのでは無いでしょうか。 [] 内の文字列は表示のみで、クリックした時に実際に飛ぶ先は () 内の URL です。
tetejiro

2024/02/17 13:22

すみません、間違えておりました。 修正いたしました。 どうぞよろしくお願いします。
jimbe

2024/02/17 14:29

ありがとうございます、リンク先見えました。
guest

回答1

0

ベストアンサー

公式のサンプルコードに質問の TypeConverter を入れて試してみました。
手抜きで EmptyActivity のプロジェクトの MainActivity にベタ書きして確認は logcat です ^^;

build.gradle(Module:app) の dependencies に以下を追加

implementation "androidx.room:room-runtime:2.6.1" annotationProcessor "androidx.room:room-compiler:2.6.1"

MainActivity.java

java

1import androidx.appcompat.app.AppCompatActivity; 2import androidx.room.Room; 3 4import android.os.*; 5import android.util.Log; 6 7import java.time.LocalDateTime; 8import java.util.concurrent.*; 9 10public class MainActivity extends AppCompatActivity { 11 private ExecutorService executor = Executors.newSingleThreadExecutor(); 12 13 @Override 14 protected void onCreate(Bundle savedInstanceState) { 15 super.onCreate(savedInstanceState); 16 setContentView(R.layout.activity_main); 17 18 executor.submit(() -> { 19 try { 20 AppDatabase db = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "database").build(); 21 UserDao userDao = db.userDao(); 22 userDao.insertAll(new User("AAA", "aaa", LocalDateTime.now())); 23 userDao.getAll().forEach(user -> Log.d("MainActivity", user.toString())); 24 } catch(Throwable t) { 25 t.printStackTrace(); 26 } 27 }); 28 } 29}

User.java

java

1import androidx.room.*; 2 3import java.time.LocalDateTime; 4 5@Entity 6public class User { 7 @PrimaryKey(autoGenerate = true) 8 public int uid; 9 10 @ColumnInfo(name = "first_name") 11 public String firstName; 12 13 @ColumnInfo(name = "last_name") 14 public String lastName; 15 16 @ColumnInfo(name = "date_time") 17 public LocalDateTime datetime; 18 19 User(String firstName, String lastName, LocalDateTime datetime) { 20 this.firstName = firstName; 21 this.lastName = lastName; 22 this.datetime = datetime; 23 } 24 25 @Override 26 public String toString() { 27 return new StringBuilder("User") 28 .append("[uid=").append(uid) 29 .append(",firstName=").append(firstName) 30 .append(",lastName=").append(lastName) 31 .append(",datetime=").append(datetime.toString()) 32 .append("]").toString(); 33 } 34}

Converters.java

java

1import androidx.room.TypeConverter; 2 3import java.time.LocalDateTime; 4 5public class Converters { 6 @TypeConverter 7 public static LocalDateTime fromDateTime(String value) { 8 return value == null ? null : LocalDateTime.parse(value); 9 } 10 11 @TypeConverter 12 public static String toDateTime(LocalDateTime datetime) { 13 return datetime == null ? null : datetime.toString(); 14 } 15}

UserDao.java

java

1import androidx.room.*; 2 3import java.util.List; 4 5@Dao 6public interface UserDao { 7 @Query("SELECT * FROM user") 8 List<User> getAll(); 9 10 @Query("SELECT * FROM user WHERE uid IN (:userIds)") 11 List<User> loadAllByIds(int[] userIds); 12 13 @Query("SELECT * FROM user WHERE first_name LIKE :first AND last_name LIKE :last LIMIT 1") 14 User findByName(String first, String last); 15 16 @Insert 17 void insertAll(User... users); 18 19 @Delete 20 void delete(User user); 21}

AppDatabase.java

java

1import androidx.room.*; 2 3@Database(entities = {User.class}, version = 1) 4@TypeConverters({Converters.class}) 5public abstract class AppDatabase extends RoomDatabase { 6 public abstract UserDao userDao(); 7}

起動する度に insert を 1 回実行して全部を表示するので 3 回目には以下のようになり、問題無さそうです。

D User[uid=1,firstName=AAA,lastName=aaa,datetime=2024-02-17T23:26:25.888] D User[uid=2,firstName=AAA,lastName=aaa,datetime=2024-02-17T23:26:41.826] D User[uid=3,firstName=AAA,lastName=aaa,datetime=2024-02-17T23:27:44.631]

TypeConverter の所は @ProvidedTypeConverter を使わない書き方で十分だろうというくらいで他に問題があるのか分かりませんが、構造の部分で onCreate で別スレッドでデータベースに接続を開始して onViewCreated でまた別スレッドでデータベースを使うというのは、接続と使用とぶつかって異常動作する可能性が高いと思います。
確実に接続が終わってから使用する ように書いたほうが良いでしょう(というよりそう書かないといけない事案です)。なお、 ConnectDB の処理は別スレッドにしなくても良い(それによる例外は出ない)ようです。

スキーマを書き換えたときに
バージョンを上げるなどの処理をしなければならないと思うのですが、
その処理を実施しなくてもいい方法

実行環境(エミュレータとか実機とか) の アプリ をアンインストールすればデータベースごと消えますので再インストールすればバージョンを変えなくても再生成されますね。

投稿2024/02/17 14:42

編集2024/02/18 15:19
jimbe

総合スコア12760

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tetejiro

2024/02/18 14:38 編集

いつも回答をいただき、本当にありがとうございます。 全然手抜きではないです。大変助かっております。 無事、こちらでも保存ができました。 前々回、回答をいただいた質問のページと今回の質問は同じ箇所でしたが、以下になりました。 熟練した方が自分と同じ機能を実装してくださり、 質問の解消だけでなく、多くを学ばせていただいております。 ありがとうございます。 >接続と使用とぶつかって異常動作する可能性が高いと思います。 こちらご指摘ありがとうございました。対応をしてみました。 >アンインストールすればデータベースごと消えますので android studio のアンインストール・再インストールをやってみたのですが、再現できず、プロジェクトを作り直して対応しました。 (きちんとした手順を勉強したいと思います。時間がかかりそうですが、、、) public class DateFragment extends Fragment { List<TimeTableEntity> timeTableEntities; List<TimeTableEntity> returnedTimeTableEntities = new ArrayList<>(); TimeTableDao timeTableDao; View view; int year; int month; int date; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); timeTableEntities = new ArrayList<>(); timeTableEntities.add(new TimeTableEntity("0番名のレコード")); timeTableEntities.add(new TimeTableEntity("1番名のレコード")); timeTableEntities.add(new TimeTableEntity("2番名のレコード")); getParentFragmentManager().setFragmentResultListener("date", this, new FragmentResultListener() { @Override public void onFragmentResult(@NonNull String key, @NonNull Bundle bundle) { year = bundle.getInt("year"); month = bundle.getInt("month"); date = bundle.getInt("date"); // 〇年・〇月・〇日をセット setDateText(); } }); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment return inflater.inflate(R.layout.fragment_date, container, false); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { this.view = view; Thread thread = new Thread(new Query()); thread.start(); try { thread.join(); // RecyclerView をセット RecyclerView recyclerView = view.findViewById(R.id.time_table_recycler_view); recyclerView.setHasFixedSize(true); recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); recyclerView.setAdapter(new Adapter()); } catch (InterruptedException e) { throw new RuntimeException(e); } } public class Query implements Runnable { @Override public void run() { AppDatabase database = Room.databaseBuilder(getActivity().getApplicationContext(), AppDatabase.class, "TimeTable").build(); timeTableDao = database.timeTableDao(); timeTableDao.insertAll(timeTableEntities); returnedTimeTableEntities = timeTableDao.getAll(); } } public void setDateText() { // 〇年をセット TextView year_text = view.findViewById(R.id.selected_year); year_text.setText(year + " 年"); // 〇月をセット TextView month_text = view.findViewById(R.id.selected_month); month_text.setText(month + " 月"); // 〇日をセット TextView date_text = view.findViewById(R.id.selected_date); date_text.setText(date + " 日"); } class Adapter extends RecyclerView.Adapter<Adapter.ViewHolder> { public class ViewHolder extends RecyclerView.ViewHolder { TextView textView; public ViewHolder(@NonNull View itemView) { super(itemView); this.textView = itemView.findViewById(R.id.every_time_table); } } @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.viewholder_time_table, parent, false); return new ViewHolder(view); } @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { holder.textView.setText(timeTableEntities.get(position).title + " / " + timeTableEntities.get(position).datetime.toString()); if (timeTableEntities.get(position).done) holder.textView.setBackgroundColor(Color.rgb(124,252,0)); // 赤 else holder.textView.setBackgroundColor(Color.rgb(249,247,57)); // 黄色 } @Override public int getItemCount() { return timeTableEntities.size(); } } }
jimbe

2024/02/18 15:15 編集

>>アンインストールすればデータベースごと消えますので >android studio のアンインストール・再インストールを(ry あ、ごめんなさい! 作成中の、実行している先(エミュレータとか実機とか)にインストールしている *アプリの* 再インストールのつもりでした。 android studio を再インストールするのは大変だったと思います。手間と時間を掛けさせてしまって申し訳ないです。
jimbe

2024/02/18 15:25

回答のコードを作っていて思ったのですが、 LocalDateTime の文字列化は toString() では無く DateTimeFormatter で "yyyy-MM-dd HH:mm:ss" の形式と変換したほうが、 SQLite の datetime 関数で SELECT 文内で直接 日時による比較(範囲選択とか)が出来て便利かもしれません。 (試してないです。)
tetejiro

2024/02/18 17:25

>*アプリの* 再インストールのつもりでした。 とんでもないです。試してみたら、確かにできました。 書き方がよく分からず詰まっており、 また今回は必要ないと思って困っていたので本当に助かりました。 時間のロスがかなり減りました。ありがとうございました。 >toString() では無く DateTimeFormatter で ご指摘ありがとうございます。 その通りですね。少し試してみたのですがまだ実現できていません。 進めながら気にかけるようにして気長に解消させていこうと思います。 今回もご回答いただき、ありがとうございました。
jimbe

2024/02/19 12:51 編集

取り合えず回答にはなっていたようで…ヨカッタデス… 気を取り直して(汗 DateFragment の onCreate で FragmentManager を妙に使われていたり、 DAO で LiveData を使えばスレッドを使わなくても済むようですので、そろそろ ViewModel や LiveData を使うことも視野に入れていっても良いかと思います。 https://github.com/Jimbe-github/tetemalu_RecordTime/tree/q_uvkoxkqhp3qe8u
tetejiro

2024/02/20 18:37 編集

>onCreate で FragmentManager を妙に使われていたり こちら onViewCreated 内に書き直しました。 ありがとうございます。 追記:すみません。勘違いしていました。 newInstance の方で対応します。ありがとうございます。 >ViewModel や LiveData 手探りでやっているもので、初めて見た単語でした。 github でこちらにも対応していただいているんですね。 何から何まで本当にありがとうございました。 本当にいつもありがとうございます。 github も必ず目を通して自分の糧とさせていただきます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.46%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問