It is my first time experimenting with Room database and I want to pre populate the database with 20 User objects. But the code I'm using adds 20 more users to the existing list everytime it runs. How do I keep it from doing that?
#Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
private static AppDatabase INSTANCE;
public abstract DataExchange dataExchange();
public static AppDatabase getAppDatabase(final Context context){
if (INSTANCE == null){
INSTANCE = Room.databaseBuilder(context,
AppDatabase.class,
"User_Database")
.addCallback(new Callback() {
#Override
public void onCreate(#NonNull final SupportSQLiteDatabase db) {
super.onCreate(db);
Executors.newSingleThreadScheduledExecutor().execute(new Runnable() {
#Override
public void run() {
getAppDatabase(context).dataExchange().insert(User.populate());
}
});
}
})
.build();
}
return INSTANCE;
}
public static void destroyInstance(){
INSTANCE = null;
}
Here's the User class:
#Entity(tableName = "user_table")
public class User{
public User(String name){
setName(name);
}
#PrimaryKey(autoGenerate = true)
private int uid;
#ColumnInfo(name = "Username")
private String name;
public String getName() {
return name;
}
public void setUid(int uid) {
this.uid = uid;
}
public void setName(String name) {
this.name = name;
}
public static User[] populate(){
User[] ar = new User[20];
for(int i=0; i<20;i++){
ar[i] = new User("USER "+(i+1));
}
return ar;
}
}
Also, where I took the code from had a getInstance(context) instead of getAppDatabase(context) inside run(). I changed it because getInstance was found in 5 libraries I didn't know about.
Here's DataExchange :
#Dao
public interface DataExchange {
#Query("SELECT * FROM user_table")
List<User> getAll();
#Query("SELECT * FROM user_table where username LIKE :name ")
User findByName(String name);
#Query("SELECT COUNT(*) from user_table")
int countUsers();
#Update
void update(User user);
#Insert
void insertAll(User... users);
#Delete
void delete(User user);
}
Step 1:
Add a primary key to your Entity, it's recommended to use an int in your scenario. For example: User with ID 1 is unique in your database.
#Entity
public class User {
#PrimaryKey
public int id;
}
https://developer.android.com/training/data-storage/room/defining-data
Step 2:
Assign a value to the id variable. I recommend assigning it through the constructor of the User class (mainly to prevent it from being 0, or to prevent you forgetting to assign it later in a setter)
Step 3:
Define a conflict strategy in your #Insert (that's in your DAO class). What do you want your database to do when it tries to add anohter User with an id = 1? Do you want to delete the older User? Just ignore it?
#Insert(onConflict = OnConflictStrategy.REPLACE)
public void insertUsers(User... users);
https://developer.android.com/reference/android/arch/persistence/room/Insert
Also, I'm 100% sure you're guessing code. Just look up the official documentation.
I recommend to just copy database which you created using DB Browser for SQLite. It is the easiest way to do prepopulating. You just create Assets folder in your app, add folder databases and after that copy your database.
In singleton class where you are creating your database just add createFromAsset() method and you have your pre-populated database. Photo is on that link.
https://i.stack.imgur.com/Q5o4v.jpg
Also in this article is described very well.
https://medium.com/androiddevelopers/packing-the-room-pre-populate-your-database-with-this-one-method-333ae190e680
Related
My code is working fine but every time people open the coin activity it takes about 5 seconds to load 500 coins from coinmarketcap api (name, symbolName, price, vol and marketcap). I used a facebook shimmer to let people know that something is loading but 5 seconds is a lot tho because the app is loading more heavy stuff within 1 to 2 seconds, for example, wordpress data with images. But, these 500 coins in 5 seconds is not cool... Here is my code...
public void getCoinList() {
//posts = 500
ApiInterface apiInterfaceCoin5 =
APIClientCoin.getClient().create(ApiInterface.class);
Map<String, String> params = new HashMap<>();
params.put("limit", posts+"");
Call<CryptoList> call5 = apiInterfaceCoin5.doGetUserListAll(params);
call5.enqueue(new Callback<CryptoList>() {
#Override
public void onResponse(Call<CryptoList> call, Response<CryptoList> response)
{
shimmerFrameLayout.stopShimmer();
shimmerFrameLayout.setVisibility(View.GONE);
swipeRefreshLayout5.setRefreshing(false);
int beforeCoinSize = cryptoList5.size();
CryptoList list5 = response.body();
cryptoList5.addAll(list5.getData());
recyclerView4.setAdapter(adapterCoin5);
}
#Override
public void onFailure(Call<CryptoList> call, Throwable t) {
//Toast.makeText(CryptoListAllCoinActivity.this, "onFailure",
Toast.LENGTH_SHORT).show();
// Log.d("XXXX", t.getLocalizedMessage());
call.cancel();
progressBar3.setVisibility(View.GONE);
shimmerFrameLayout.setVisibility(View.GONE);
swipeRefreshLayout5.setRefreshing(false);
}
});
}
#Headers("X-CMC_PRO_API_KEY: HIDDEN")
#GET("/v1/cryptocurrency/listings/latest")
Call<CryptoList> doGetUserListAll(#QueryMap Map<String, String> params);
Adapter:
// Involves populating data into the item through holder
// binds the data to the TextView in each row
#Override
public void onBindViewHolder(ViewHolder holder, int
position) {
// Get the data model based on position
Datum datum = mData.get(position);
//load coin icon
//if the link doesn't work then you have to upload it into
your own server
Glide.with(context)
.load(new
StringBuilder
("https://s2.coinmarketcap.com/static/img/coins/64x64/")
.append(datum.getId())
.append(".png").toString())
.placeholder(R.drawable.money_icon).into(holder.coin_icon);
Glide.with(context)
.load(new
StringBuilder
("https://s3.coinmarketcap.com/generated/
sparklines/web/7d/usd/")
.append(datum.getId())
.append(".png").toString())
.placeholder(R.drawable.line_24)
.into(holder.sparkline);
TextView symbolName = holder.symbolName;
symbolName.setText(datum.getSymbol());
// Set item views based on your views and data model
TextView name = holder.name;
name.setText(datum.getName());
TextView price = holder.price;
TextView priceDetails = holder.priceDetails;
TextView marketCap = holder.marketCap;
marketCap.setText("$" +
formatNumber(datum.getQuote().getUSD().getMarketCap()));
ImageView coin_icon = holder.coin_icon;
ImageView sparkline = holder.sparkline;
if(datum.getQuote().getUSD().getPrice() >= 1) {
price.setText("$" +
formatNumber(datum.getQuote().getUSD().getPrice()));
}else{
price.setText("$" + String.format("%f",
datum.getQuote().getUSD().getPrice()));
}
TextView textView24h = holder.textView24h;
textView24h.setText(String.format("%.2f",
datum.getQuote().getUSD().getPercentChange24h()) + "%");
if(datum.getQuote().getUSD().getPercentChange24h() <0.000).{
//red
textView24h.setTextColor(Color.parseColor("#EA3943"));
arrowImage.setImageResource(R.drawable.arrow_down);
sparkline.setColorFilter(Color.RED,
PorterDuff.Mode.MULTIPLY);
sparkline.setImageResource(R.drawable.btc_spike);
//changeImageColor(context, sparkline,000);
}else{
//green
textView24h.setTextColor(Color.parseColor("#18C784"));
arrowImage.setImageResource(R.drawable.arrow_up);
sparkline.setColorFilter(Color.GREEN,
PorterDuff.Mode.MULTIPLY);
sparkline.setImageResource(R.drawable.btc_spike);
}
}
Glide.with(context)
.load(new
StringBuilder
("https://s2.coinmarketcap.com/static/img/coins/64x64/")
.append(datum.getId())
.append(".png").toString())
.placeholder(R.drawable.money_icon)
.into(holder.coin_icon);
UPDATE
Now I have the database setup but not sure how to insert data into the database...for example:
where do I add this:
AppDatabase db =
AppDatabase.getDbInstance(this.getApplicationContext());
//coin object
db.coinDao().insertAllCoins();
inside the onResponse? and how?
UPDATE 8-5-2021:
AppDatabase.class
#Database(entities = {Coins.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract CoinDao coinDao();
private static AppDatabase INSTANCE;
public static AppDatabase getDbInstance(Context context){
if(INSTANCE == null){
INSTANCE = Room
.databaseBuilder(context.getApplicationContext(),
AppDatabase.class,"DB_COIN")
.allowMainThreadQueries()
.build();
}
return INSTANCE;
}
}
CoinDao
#Dao
public interface CoinDao {
#Query("SELECT * FROM coin_table")
List<Coins> getAll();
#Insert
void insertAllCoins(Coins... coins);
#Delete
void delete(Coins coins);
#Update
void updateUsers(Coins... coins);
}
Coins.java
#Entity(tableName = "coin_table")
public class Coins {
#PrimaryKey(autoGenerate = true)
public int id;
#ColumnInfo(name = "name")
public String name;
#ColumnInfo(name = "symbol")
public String symbol;
#ColumnInfo(name = "slug")
public String slug;
#ColumnInfo(name = "circulating_supply")
public Double circulatingSupply;
#ColumnInfo(name = "total_supply")
public Double totalSupply;
#ColumnInfo(name = "max_supply")
public Double maxSupply;
#ColumnInfo(name = "date_added")
public String dateAdded;
#ColumnInfo(name = "num_market_pairs")
public Integer numMarketPairs;
#ColumnInfo(name = "cmc_rank")
public Integer cmcRank;
#ColumnInfo(name = "coin_symbol")
public String lastName;
#ColumnInfo(name = "last_updated")
public String lastUpdated;
#ColumnInfo(name = "price")
public Double price;
#ColumnInfo(name = "volume_24h")
public Double volume24h;
#ColumnInfo(name = "percent_change_1h")
public Double percentChange1h;
#ColumnInfo(name = "percent_change_24h")
public Double percentChange24h;
#ColumnInfo(name = "percent_change_7d")
public Double percentChange7d;
#ColumnInfo(name = "market_cap")
public Double marketCap;
}
UPDATE 8-6-2021 Base on Muhammad Shuja answer
For the CoinRepository I am getting this error:
But if I change it to CriptoList then it says it require a List lol.... Any idea why?
Please note I use Coins with the s because that is the class name.
also if I change it to CriptoList it says that this
coinDao.insertAll(response.body());
require a List
I have my public getCoinList() a lil bit up for reference on how I am currently getting the data from the api.
And, yes I want to update the data every 1...like I would like to make an api call and update the data every 1 minute. Thankssssssssss
Create a CoinRepository and implement your logic there. If DB is empty or it is required to refresh data, let your repo query data from API and insert it in DB, and then fetch it from DB whenever needed.
Check this codelab to practically understand Room.
Create Coin entity, it's a good practice to keep entity/model classes name singular, and their respective table names plural.
Coin.java
#Entity(tableName = "coins")
public class Coin {
#PrimaryKey(autoGenerate = true)
public int id;
#ColumnInfo(name = "name")
public String name;
#ColumnInfo(name = "symbol")
public String symbol;
#ColumnInfo(name = "slug")
public String slug;
#ColumnInfo(name = "circulating_supply")
public Double circulatingSupply;
#ColumnInfo(name = "total_supply")
public Double totalSupply;
#ColumnInfo(name = "max_supply")
public Double maxSupply;
#ColumnInfo(name = "date_added")
public String dateAdded;
#ColumnInfo(name = "num_market_pairs")
public Integer numMarketPairs;
#ColumnInfo(name = "cmc_rank")
public Integer cmcRank;
#ColumnInfo(name = "coin_symbol")
public String lastName;
#ColumnInfo(name = "last_updated")
public String lastUpdated;
#ColumnInfo(name = "price")
public Double price;
#ColumnInfo(name = "volume_24h")
public Double volume24h;
#ColumnInfo(name = "percent_change_1h")
public Double percentChange1h;
#ColumnInfo(name = "percent_change_24h")
public Double percentChange24h;
#ColumnInfo(name = "percent_change_7d")
public Double percentChange7d;
#ColumnInfo(name = "market_cap")
public Double marketCap;
}
Create CoinDao interface and add your query methods in it, I've added additional methods like get(id) and update(coin), you can use them if needed.
CoinDao.java
#Dao
public interface CoinDao {
#Query("SELECT * FROM coins")
LiveData<List<Coin>> getAll();
#Query("SELECT * FROM coins WHERE id = :id")
LiveData<Coin> get(int id);
#Insert
void insertAll(List<Coin> coins);
#Update
public void update(Coin coin);
#Query("DELETE FROM coins")
void deleteAll();
}
Create your database class as follows:
DB.java
#Database(entities = {Coin.class}, version = 1)
public abstract class DB extends RoomDatabase {
private static final String TAG = "DB";
// DB INFO
private static final String databaseName = "coin_database";
// DAOs
public abstract CoinDao coinDao();
// INSTANCE
private static volatile DB INSTANCE;
private static final int NUMBER_OF_THREADS = 4;
public static final ExecutorService databaseWriteExecutor =
Executors.newFixedThreadPool(NUMBER_OF_THREADS);
public static DB getInstance(final Context context) {
if (INSTANCE == null) {
synchronized (DB.class) {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(context.getApplicationContext(), DB.class, databaseName)
.build();
Log.d(TAG, "New instance created...");
}
}
}
return INSTANCE;
}
}
Create CoinRepository and add your WebAPI/DB logic in it. Notice, I've moved your API call in getCoins() method, you need to modify it according to your logic. Make sure your call response returns List<Coin>, or you'll have to create a List<Coin> from CryptoList yourself.
CoinRepository.java
public class CoinRepository {
private static final String TAG = "Repo/Coin";
private final CoinDao coinDao;
private final LiveData<List<Coin>> coins;
public CoinRepository(Application application) {
coinDao = DB.getInstance(application).coinDao();
coins = coinDao.getAll();
Log.d(TAG, "New instance created...");
}
/*
I've added boolean flag to check if data reload from Web API is compulsory.
You can remove this flag and modify it as per your logic.
DataReadyListener is a callback listener.
Its onDataReady() method gets fired when data is ready.
*/
public void getCoins(boolean reload, DataReadyListener listener) {
if(reload){
//Modify this portion as per your logic
ApiInterface apiInterfaceCoin5 =
APIClientCoin.getClient().create(ApiInterface.class);
Map<String, String> params = new HashMap<>();
params.put("limit", posts+"");
apiInterfaceCoin5.doGetUserListAll(params)
.enqueue(new Callback<List<Coin>>() {
#Override
public void onResponse(Call<List<Coin>> call, Response<List<Coin>> response) {
Future<?> future = DB.databaseWriteExecutor.submit(() -> {
coinDao.deleteAll(); //remove this if you want to keep previous data
coinDao.insertAll(response.body());
Log.d(TAG, "Data inserted in \"coins\" table");
});
try {
future.get();
if (future.isDone())
listener.onDataReady(coins);
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
#Override
public void onFailure(Call<List<Coin>> call, Throwable t) {
//Handle failure here
}
});
}
else{
listener.onDataReady(coins);
}
}
public LiveData<Coin> getCoin(int id) {
return coinDao.get(id);
}
public interface DataReadyListener {
void onDataReady(LiveData<List<Coin>> coins);
}
}
And lastley create CoinViewModel. You can completely skip this step and query data directly from CoinRepository. But ViewModel has its own pros, check docs for details.
CoinViewModel.java
public class CoinViewModel extends AndroidViewModel {
private CoinRepository repository;
public CoinViewModel(#NonNull Application application) {
super(application);
repository = new CoinRepository(application);
}
public void getCoins(boolean reload, CoinRepository.DataReadyListener listener) {
repository.getCoins(reload, listener);
}
public LiveData<Coin> getCoin(int id) {
return repository.getCoin(id);
}
}
Now use CoinViewModel in your Activity/Fragment, first initialize it:
UI (Activity/Fragment)
private CoinViewModel coinViewModel;
#Override
protected void onCreate(Bundle savedInstanceState) {
....
coinViewModel = new ViewModelProvider(this).get(CoinViewModel.class);
....
}
And finally use your ViewModel to query data. If you want to reload the data from Web API send first parameter as true, if you want to reuse existing data from DB keep flag as false:
coinViewModel.getCoins(true, new CoinRepository.DataReadyListener() {
#Override
public void onDataReady(LiveData<List<Coin>> coins) {
//Add or set data in adapter
//for setData() you need to create setData() method in adapter
//which first clears existing data and then adds new data to list and notifies about dataset change.
coins.observe(this, coinsList -> coinsAdapter.setData(coinsList));
}
});
Hopefully, it helps.
Update 07/08/2021
After discussion with OP, I've updated the CoinRepository code. Now, instead of relying on boolean flag, it automatically updates data after 1 minute interval using RxAndroid:
public class CoinRepository {
private static final String TAG = "Repo/Coin";
private final CoinDao coinDao;
private final LiveData<List<Coin>> coins;
public CoinRepository(Application application) {
coinDao = DB.getInstance(application).coinDao();
coins = coinDao.getAll();
loadCoinsPeriodically();
Log.d(TAG, "New instance created...");
}
public LiveData<List<Coin>> getCoins() {
return coins;
}
private void loadCoinsPeriodically() {
Observable.interval(0, 1, TimeUnit.MINUTES)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
aLong ->
RetrofitService.getClient().getLatest(500)
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(
cryptoList ->
DB.databaseWriteExecutor.submit(() -> {
coinDao.deleteAll();
coinDao.insertAll(cryptoList.getCoins());
Log.d(TAG, "Data inserted in \"coins\" table");
}),
throwable ->
Log.d(TAG, "API observable error: " + throwable.getMessage())),
throwable ->
Log.d(TAG, "Periodic observable error: " + throwable.getMessage()));
}
public LiveData<Coin> getCoin(int id) {
return coinDao.get(id);
}
}
I've written an example app for OP's use case, check this Github repo for complete code.
OkHttp client can make 64 requests in parallel by default. You can play with that value and increase maxRequests number. Create new OkHttp dispatcher:
Dispatcher dispatcher = new Dispatcher();
dispatcher.setMaxRequests(200);
Then, initialize OkHttp client:
OkHttpClient client = new OkHttpClient.Builder()
...
.dispatcher(dispatcher)
.build();
Finally, add this OkHttp into Retrofit:
Retrofit retrofit = new Retrofit.Builder()
...
.client(client)
.build();
i have problem with saving data in DB.I'm new in Spring Boot. When i run my program the result of writen data is: packagename#randomcode example:com.abc.patient.Patient#6e3e681e
This is my Entity class - Patient.java
#Entity
public class Patient {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
// getter, setter, constructor, etc
}
This is my CrudRepo PatientRepository.java
public interface PatientRepository extends CrudRepository<Patient,Integer> {
}
This is my Service class PatientService.java
#Service
public class PatientService {
#Autowired
private PatientRepository patientRepository;
public void savePatient (String name) {
Patient patient = new Patient(name);
patientRepository.save(patient);
}
public Optional<Patient> showPatient(int id) {
return patientRepository.findById(id);
}
public List<Patient> showAllPatients() {
List<Patient> patients = new ArrayList<>();
patientRepository.findAll().forEach(patients::add);
return patients;
}
}
I think that problem in in the savePatient method in this line:
Patient patients = new Patient(name);
I checked the "name" parameter and it's in 100% correct String. I'm using Derby DB.
The only problem you have is how you are printing out your Patient class. Define a proper toString() or just debug yourself to see the resulting fields. There is no problem in your JPA implementation.
See this question for the details of default toString
Try:
public void savePatient(Patient patient) {
patientRepository.save(patient);
}
in my project I have a library module and an application module using it. In both modules I have the same gradle dependencies on Android Architecture Components library:
// ViewModel and LiveData
implementation "android.arch.lifecycle:extensions:1.0.0"
implementation "android.arch.lifecycle:common-java8:1.0.0"
// Room
implementation "android.arch.persistence.room:runtime:1.0.0"
annotationProcessor "android.arch.persistence.room:compiler:1.0.0"
In my library module I have defined a User entity
#Entity(tableName = "users",
indices = {#Index(value = {"firstName", "lastName"})})
public class User {
public enum ROLE {
...
}
public enum FEEDBACK_LEVEL {
...
}
#PrimaryKey
public int id;
#TypeConverters(UserConverters.class)
ROLE role;
#TypeConverters(UserConverters.class)
FEEDBACK_LEVEL feedbackLevel;
public String firstName;
public String lastName;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public ROLE getRole() {
return role;
}
public void setRole(ROLE role) {
this.role = role;
}
public FEEDBACK_LEVEL getFeedbackLevel() {
return feedbackLevel;
}
public void setFeedbackLevel(FEEDBACK_LEVEL feedbackLevel) {
this.feedbackLevel = feedbackLevel;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
and the related DAO interface
#Dao
public interface UserDAO {
#Insert(onConflict = OnConflictStrategy.REPLACE)
void insertUser(User ... u);
#Query("select * from users where users.id = :userId")
LiveData<User> getUser(int userId);
}
In my application module I've created my database in which I'm using the entity defined in the library project
#Database(entities = {User.class}, version = 1)
public abstract class TDatabase extends RoomDatabase{
private static TDatabase sInstance;
public static TDatabase getInstance(final Context c) {
if(sInstance == null)
sInstance = Room.databaseBuilder(c, TDatabase.class, "t_db").build();
return sInstance;
}
public abstract UserDAO userDao();
}
The problem is that when I try to refer to a method parameter in a #Querystatement using its name I get the following error
Error:Each bind variable in the query must have a matching method parameter. Cannot find method parameters for :userId.
If I change the #Query from
#Query("select * from users where users.id = :userId")
LiveData<User> getUser(int userId);
to
#Query("select * from users where users.id = :arg0")
LiveData<User> getUser(int userId);
everything works fine.
Am I doing some mistakes? Why I'm getting this error?
I've googled for a solution but I found only results referring to Kotlin while I'm using Java.
This issue reported at https://issuetracker.google.com/issues/68118746 and fixed in version 1.1.0-alpha3, but only for Kotlin code since parameter names are always stored in Kotlin classes metadata.
For Java, there is only workaround with :arg0 until annotation like NamedArg will be added to Room.
I think you should use it like that:
#Query("select * from users where id = (:userId)")
LiveData<User> getUser(int userId);
This problem still pops up for me sometimes when working on db logic, but "Clean Project" usually fixes it in IntelliJ IDEA (should work just the same in Android Studio).
My professor gave a sample Spring MVC ORM project with Hibernate but I can not figure out the sequence of events involved, in particular about the usage of service business object.
This is just a little part of the project, just to make my ideas clearer.
domain:
#Entity
#Table(name = "department")
public class Department implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
private Long uid;
private String name;
#OneToMany(mappedBy="department",cascade=CascadeType.PERSIST)
private List<Employee> employees = new ArrayList<Employee>();
public Department() {
}
public Department(String name) {
this.name = name;
}
// getters, setters, hashcode() and equals(), toString()...
controller:
#Controller
#RequestMapping("/department")
public class DepartmentController {
#Autowired
#Qualifier("departmentBO")
private DepartmentBO departmentBO;
static final Logger logger = Logger.getLogger(DepartmentController.class);
#RequestMapping(value = "/home", method = RequestMethod.GET)
public String departmentHome(Model model) {
logger.debug("department home() invoked");
List<Department> list = departmentBO.findAllDepartments();
model.addAttribute("list", list);
return "departments";
}
// i'll paste just the first controller ;)
business:
public interface DepartmentBO {
public void delete(long uid);
public List<Department> findAllDepartments();
public Department findByUid(Long uid);
public void save(Department department);
public void update(Department department);
}
business/impl:
#Service
#Transactional
public class DepartmentBoImpl implements DepartmentBO {
#Autowired
private DepartmentDAO departmentDao;
static final Logger logger = Logger.getLogger(DepartmentBoImpl.class);
#Override
public void save(Department department) {
departmentDao.save(department);
}
#Override
public void update(Department department) {
departmentDao.update(department);
}
#Override
public void delete(long uid) {
departmentDao.delete(uid);
}
#Override
public List<Department> findAllDepartments() {
return departmentDao.findAllDepartments();
}
#Override
public Department findByUid(Long uid) throws DataAccessException {
return departmentDao.findByUid(uid);
}
}
dao:
public interface DepartmentDAO {
public void delete(long uid);
public List<Department> findAllDepartments();
public Department findByUid(Long uid);
public void save(Department user);
public void update(Department user);
}
dao/impl:
#Repository
public class DepartmentDAOImplSf implements DepartmentDAO {
#Autowired
private SessionFactory sessionFactory;
#Override
public void delete(long uid) {
Department department = (Department) sessionFactory.getCurrentSession()
.get(Department.class, uid);
sessionFactory.getCurrentSession().delete(department);
}
#Override
public void save(Department department) {
sessionFactory.getCurrentSession().save(department);
}
#Override
public void update(Department department) {
sessionFactory.getCurrentSession().saveOrUpdate(department);
}
#Override
public List<Department> findAllDepartments() {
List<Department> list = (List<Department>) sessionFactory
.getCurrentSession()
.createQuery("FROM Department").list();
return list;
}
#Override
public Department findByUid(Long uid) {
Department department = (Department) sessionFactory
.getCurrentSession().get(Department.class, uid);
return department;
}
}
I know that the order is: domain model -> controller-> service -> dao ->db, but why use a DepartmentBO? and why DepartmentBoImpl autowired DepartmentDao? Who of them act first? Something that i'm not understanding is messing up my conception of how it works and the sequence of the process..
Thanks for your help ;)
EDIT: "
In few words my question is, what is the sequence of this code? user goes on the /home page that redirect on "departments" page. But what happen before this --> "List list = departmentBO.findAllDepartments();" ?;)
When the departmentBO.findAllDepartments() method is called if you look at the code it invokes the sessionFactory. That is an internal factory class in Hibernate that basically builds a transactional connection to the DB in order to run a query. You are defining the query in the createQuery method and then ultimately executing it with the list() method. These two methods are part of the database session that Hibernate has instantiated.
Departments Page -> departmentBO.findAllDepartments() -> sessionFactory -> createQuery -> list()
Or in pseudo code-ish
Departments Page -> execute findAllDepartments method -> fetch / build a database connection -> define the query -> execute the query -> Return the list!
First of all, sorry for my english.
Im making, for a friend, a desktop application with JPA(EclipseLink) that access a SQLite database.
I already created the database and the entities in Eclipse. But i also created a class called UniversalDAO which is an utility class used by all the entities to access and persist the database:
package model.DAO;
import java.util.ArrayList;
import javax.persistence.*;
import model.entities.Entities;
public class UniversalDAO {
private static EntityManagerFactory emf = Persistence.createEntityManagerFactory("TheDatabase");
private static EntityManager em = emf.createEntityManager();
private UniversalDAO (){}
public static void close(){
em.close();
emf.close();
}
public static Entities getOne(Class<? extends Entities> table, Object primaryKey) {
return em.find(table, primaryKey);
}
public static ArrayList<Entities> getAll(Class<? extends Entities> table) {
ArrayList<Entities> ret = new ArrayList<>();
for(Object obj : em.createQuery("SELECT o FROM " + table.getName() + " o").getResultList())
ret.add((Entities) obj);
return ret;
}
public static ArrayList<Entities> getWithCondition(Class<? extends Entities> table, String condition) {
ArrayList<Entities> ret = new ArrayList<>();
for(Object obj : em.createQuery("SELECT o FROM " + table.getName() + " o WHERE " + condition).getResultList())
ret.add((Entities) obj);
return ret;
}
public static void insert(Entities row) {
em.getTransaction().begin();
em.persist(row);
em.flush();
em.getTransaction().commit();
}
public static void update(Entities row) {
em.getTransaction().begin();
em.merge(row);
em.flush();
em.getTransaction().commit();
}
public static void delete(Class<? extends Entities> table, Object primaryKey) {
em.getTransaction().begin();
Entities row = em.find(table, primaryKey);
em.remove(row);
em.flush();
em.getTransaction().commit();
}
}
To group all the entites and use them in this class i created an empty interface called Entities.
This is how one of the entities looks like:
package model.entities;
import java.util.ArrayList;
import javax.persistence.*;
#Entity
#Table(name="emails")
public class EntityEmail implements Entities {
#Id
#Column(name="id_email")
#GeneratedValue(strategy=GenerationType.SEQUENCE)
private int idEmail;
#Column(name="email")
private String email;
#Column(name="description")
private String description;
#ManyToMany(fetch=FetchType.EAGER)
#JoinTable(name="people_emails",
joinColumns=#JoinColumn(name="id_email", referencedColumnName="id_email"),
inverseJoinColumns=#JoinColumn(name="id_person", referencedColumnName="id_person"))
private ArrayList<EntityPerson> people;
public EntityEmail() {
}
public int getIdEmail() {
return this.idEmail;
}
public void setIdEmail(int idEmail) {
this.idEmail = idEmail;
}
public String getEmail() {
return this.email;
}
public void setEmail(String email) {
this.email = email;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public ArrayList<EntityPerson> getPeople() {
return people;
}
public void setPeople(ArrayList<EntityPerson> people) {
this.people = people;
}
}
Like you can appreciate im not a professional and i still have a lot to learn.
So, i was wondering if this approach is correct or if i should have one DAO for each entity.
It looks like you try to invent Generic DAO pattern. If so, you're essentially on the right way.
Generic DAO pattern works as follows:
Create a generic base class that all your DAOs will extend:
public abstract class GenericDao<E, ID extends Serializable> {
...
// Implement common operations that are relevant to all entities here:
public E findById(ID id) { ... }
public void save(E entity) { ... }
// etc
...
}
Create concrete DAO implementations by extending GenericDao:
public class EntityEmailDao extends GenericDao<EntityEmail, Integer> {
// This class may contain operations that are relevant to specific entity:
public E findByEmail(String email) { ... }
}
Since GenericDao is generic, you don't need to make your entities extend any common interface
There are plenty of exisiting implementations of this pattern around, take a look, for example, here.