Good morning/afternoon/evening,
I'm a Java beginner, but I've been tasked with making a database application.
In that application, I'd like to create a class that returns other objects when an instance of that class is created:
public LoadStudents(String query){
File studentsFolder = new File("Data/Students/");
try{
switch (query){
case "all": // additional cases might be added in the future
//- that's why I am using the class in the first place
for (final File entry : studentsFolder.listFiles()){
if(!entry.isDirectory()){
FileInputStream in = new FileInputStream(entry);
ObjectInputStream object = new ObjectInputStream(in);
Student[] student = (Student[]) object.readObject();
object.close();
}
}
break;
default:
final File entry = new File("Data/Students/"+query+".stud");
FileInputStream in = new FileInputStream(entry);
ObjectInputStream object = new ObjectInputStream(in);
Student searched = (Student) object.readObject();
}
}
catch(Exception load){
load.printStackTrace();
}
}
My question is: Can the student objects can be accessed when I create the LoadStudents object? If so, how?
LoadStudents load = new LoadStudents("something");
load.searched.doSomething();
maybe?
P.S. I'm sorry if this is a noobish question, but I'd like to know the answer nevertheless.
If the public methods of a class return Objects (any kind of Object) then other classes can use it without restriction. There are other rules, but that's the primary one to consider when beginning Java programming.
Also, I recommend that you adopt (in the beginning at least) a different style of organizing your solution.
Make your class names reflect things.
Make your method names on the class reflect what you can do with that thing.
so
public class Database {
.....
public Student getStudentById(String id) {
....
}
....
}
which then get used like so
Database database = ....
Student bob = database.getStudentById("2323");
or something like that.
With this approach, then you could add a "StudentListener" interface, like so
public interface StudentListener {
....
public void studentAdded(Details ...);
}
And make the database accept student listeners.
(in Database)
public void addListener(StudentListener listener) {
...
}
Then when a student addition is done calling the additional lines
for (StudentListener listener : listeners) {
listener.studentAdded(details);
}
will have the listeners be notified by the database that a student was added.
The pattern above is known as the Listener pattern, and you should learn it. It is very useful in many situations, and you will see it again.
Add Student searched object to class.
ex.
public Student searched;
In constructor set object to searched field. You dont need to create new object.
Related
how are you? I'm trying to do some dynamic method calls to get sql strings on various objects in Java (Android), but i'm stuck with some questions about performance and stability.
Context Example: Repository class onCreate method get all entity objects (tables) and call a method (getCreateTable for example) to get a sql string to execute.
Sure i can explicit call class by class caling each method, but i have other calls like "dropTables", "truncateTables" and etc, and i do not want to be repeating the same structure all the time.
public void CreateTables() {
execute(Entity1.getCreateTable());
execute(Entity2.getCreateTable());
execute(Entity3.getCreateTable());
[..]
execute(Entity50.getCreateTable());
}
public void DropTables() {
execute(Entity1.getDropTable());
execute(Entity2.getDropTable());
execute(Entity3.getDropTable());
[..]
execute(Entity50.getDropTable());
}
Until now i know i can do that in 3 diferent ways.
1) Using reflection (currently in use): Basicaly, i store all the objects class in a list, and then use the reflection to call the desired static method.But i know that reflection not always should be the first choice.
private final List<Class> entityList = new ArrayList<Class>() {
{
add(Entity1.class);
add(Entity2.class);
add(Entity3.class);
}
};
public void createTables() {
/* get all query strings */
List<String> queryList = getQueryList("createTable");
try {
for (String query : queryList) {
execute(query);
}
} catch (SQLException e) {
[...]
}
}
private List<String> getQueryList(String methodName) {
List<String> queryList = new ArrayList<>();
for (Class<?> objectClass : entityList) {
try {
Method[] ms = objectClass.getMethods();
for (Method me : ms) {
if (me.getName().equals(methodName)) {
String query = (String) me.invoke(null);
if (query != null && query.length() > 0) {
queryList.add((String) me.invoke(null));
}
break;
}
}
} catch (Exception e) {
[...]
}
}
return queryList;
}
2) Storing object instance in list: I can have a list with the objects instanced and then cast then into abstract parent class (or interface) and call the methods to get the sql string. In this case, i don't know if is a good practice to keep an list of instanced objects in memory, maybe this could be worst than use reflection depending on list size.
private final List<BaseEntity> entityList = new ArrayList<BaseEntity>() {
{
add(new Entity1(context));
add(new Entity2(context));
add(new Entity3(context));
}
};
public void createTables() {
for (BaseEntity entity : entityList) {
try {
execute(entity.getCreateTable());
} catch (Exception e) {
[...]
}
}
}
3) Storing all the strings into JSON object: I don't tested that one yet, but i'm sure with should work. I can call an "init" method to iterate over all objects and create that JSON object/array with all the sql strings (drop, create, truncate and etc).
I really appreciate if you share with me what you think about these approaches (pros and cons) or another better solution.
As pointed out in the comments, it was a clarly a bad design (it's an old project that i'm refactoring). So i decided to get away from reflection and spend some time to redesign the code itself.
I created an base super class to handle all similiar methods and let the entities/models implement only the required individual rules, so DB access is stored in only one class as a Singleton. It's far better to use interface polymorphism.
In this way, the db class handle the dinamic SQL generation to avoid repeating the same code everywhere and re-use/recycle the list of instances to improve performance.
Obs. 1: Reflection throw down performance and usually let debbuging harder. Sure it can save some time as it is fast to implement, but will disable most of the IDE features, which makes it worthless in most cases.
Obs. 2: Keeping a list of DB instances active should never be done either. It's never a good idea to have many instances access database simultaneously, it can cause DB to lock and reproduce unexpectad issues.
Obs. 3: That JSON thing... forget about it. I'm sorry to suggest something so ugly.
OK, so I have an interesting problem. I am using java/maven/spring-boot/cassandra... and I am trying to create a dynamic instantiation of the Mapper setup they use.
I.E.
//Users.java
import com.datastax.driver.mapping.annotations.Table;
#Table(keyspace="mykeyspace", name="users")
public class Users {
#PartitionKey
public UUID id;
//...
}
Now, in order to use this I would have to explicitly say ...
Users user = (DB).mapper(Users.class);
obviously replacing (DB) with my db class.
Which is a great model, but I am running into the problem of code repetition. My Cassandra database has 2 keyspaces, both keyspaces have the exact same tables with the exact same columns in the tables, (this is not my choice, this is an absolute must have according to my company). So when I need to access one or the other based on a form submission it becomes a mess of duplicated code, example:
//myWebController.java
import ...;
#RestController
public class MyRestController {
#RequestMapping(value="/orders", method=RequestMethod.POST)
public string getOrders(...) {
if(Objects.equals(client, "first_client_name") {
//do all the things to get first keyspace objects like....
FirstClientUsers users = (db).Mapper(FirstClientUsers.class);
//...
} else if(Objects.equals(client, "second_client_name") {
SecondClientUsers users = (db).Mapper(SecondClientUsers.class);
//....
}
return "";
}
I have been trying to use methods like...
Class cls = Class.forName(STRING_INPUT_VARIABLE_HERE);
and that works ok for base classes but when trying to use the Accessor stuff it no longer works because Accessors have to be interfaces, so when you do Class cls, it is no longer an interface.
I am trying to find any other solution on how to dynamically have this work and not have to have duplicate code for every possible client. Each client will have it's own namespace in Cassandra, with the exact same tables as all other ones.
I cannot change the database model, this is a must according to the company.
With PHP this is extremely simple since it doesn't care about typecasting as much, I can easily do...
function getData($name) {
$className = $name . 'Accessor';
$class = new $className();
}
and poof I have a dynamic class, but the problem I am running into is the Type specification where I have to explicitly say...
FirstClientUsers users = new FirstClientUsers();
//or even
FirstClientUsers users = Class.forName("FirstClientUsers");
I hope this is making sense, I can't imagine that I am the first person to have this problem, but I can't find any solutions online. So I am really hoping that someone knows how I can get this accomplished without duplicating the exact same logic for every single keyspace we have. It makes the code not maintainable and unnecessarily long.
Thank you in advance for any help you can offer.
Do not specify the keyspace in your model classes, and instead, use the so-called "session per keyspace" pattern.
Your model class would look like this (note that the keyspace is left undefined):
#Table(name = "users")
public class Users {
#PartitionKey
public UUID id;
//...
}
Your initialization code would have something like this:
Map<String, Mapper<Users>> mappers = new ConcurrentHashMap<String, Mapper<Users>>();
Cluster cluster = ...;
Session firstClientSession = cluster.connect("keyspace_first_client");
Session secondClientSession = cluster.connect("keyspace_second_client");
MappingManager firstClientManager = new MappingManager(firstClientSession);
MappingManager secondClientManager = new MappingManager(secondClientSession);
mappers.put("first_client", firstClientManager.mapper(Users.class));
mappers.put("second_client", secondClientManager.mapper(Users.class));
// etc. for all clients
You would then store the mappers object and make it available through dependency injection to other components in your application.
Finally, your REST service would look like this:
import ...
#RestController
public class MyRestController {
#javax.inject.Inject
private Map<String, Mapper<Users>> mappers;
#RequestMapping(value = "/orders", method = RequestMethod.POST)
public string getOrders(...) {
Mapper<Users> usersMapper = getUsersMapperForClient(client);
// process the request with the right client's mapper
}
private Mapper<Users> getUsersMapperForClient(String client) {
if (mappers.containsKey(client))
return mappers.get(client);
throw new RuntimeException("Unknown client: " + client);
}
}
Note how the mappers object is injected.
Small nit: I would name your class User in the singular instead of Users (in the plural).
I have such IDL interface:
interface User
{
string toString();
//..
};
interface Group
{
typedef sequence<User> Users;
Users getUsers();
};
When I translated it to C++ I got sth like this:
// ...
Group::Users* GroupImpl::getUsers()
{
// ..return sequence of 'User'-objects
}
On client side (written in Java) I want to show my users. I do sth like this:
public void showAllUsers()
{
User[] users = interface_obj.getUsers();
if(users.length != 0)
{
DefaultListModel model = new DefaultListModel();
for(int i=0; i<users.length; i++)
model.addElement(users[i]);
this.usersList.setModel(model);
}
}
this.usersList is a JList.
When I do this like I wrote, I see only IORs of my Users-object:
IOR :0123405948239481293812312903891208320131293812381023
IOR: 0092930912617819919191818173666288810010199181919919
and so on ...
How to make it that way, to see their toString(); representation in DefaultListModel? I dont want to do this:
model.addElement(users[i].toString());
thats not the point. When I use RMI instead of CORBA, model.addElement(users[i]); is exactly what I need cause I see users string representation.
But I need to use CORBA and store in DefaultListModel corba-user-objects, not strings. Please, help.
I guess that the toString() method of the stub doesn't actually call the toString() method of the remote CORBA object. Try using another method name (like getName()), and use a custom renderer which calls this method.
That said, is it really a good idea to model a User as a remote CORBA object? That will cause a lot or remote method calls just to display the names of the users, and thse method calls are basically out of your control, since the Swing components will make them. Shouldn't you use DTOs instead?
One way to do it would be to make a UserView class whose instances you'd put in the list model:
public class UserView {
private final User corbaUser;
public UserView(User corbaUser) {
this.corbaUser = corbaUser
}
#Override
public String toString() {
String ret = null;
// construct the string as you want here
return ret;
}
}
EDIT:
as pointed out by JB Nizet be careful with the code you put in toString() since it is called every time the list needs to be shown - or the showing of the freshest data might be exactly what you want.
I have a method where I want to factor out some code into its own method
This is what I have:
public class TD0301AssignmentForm extends Form {
public TD0301AssignmentForm(TD0301AssignmentDAO dao, STKUser authenticatedUser) {
this.dao = dao;
this.authenticatedUser = authenticatedUser;
}
public Object insert(HttpServletRequest request) {
TD0301Assignment tdas = new TD0301Assignment();
TD0301Assignment tdas_orig = null;
Date dateNow = new Date();
try {
// Get the inuput from HTML form
tdas.setCalc_num(FormUtil.getFieldValue(request, FIELD_CALC_NUM));
processDate(request, tdas);
tdas.setCalc_dept(FormUtil.getFieldValue(request, FIELD_CALC_DEPT));
tdas.setYear_oi(Integer.toString(DateUtil.getIntYear(dateNow)));
processCalcSafetyRequirements(request, tdas);
...etc...
if (isSucces()) {
// Instantiate a base work flow instance!
WorkflowInstance wfi = new WorkflowInstance();
WorkflowInstanceDAO wfiDAO = new WorkflowInstanceDAO();
wfi.setWorkflow_class_id(tdas.getCalc_level());
wfi.setStarted_by(authenticatedUser.getBadge());
wfi.setStatus("0");
wfi.setLast_date(dateNow);
// Insert the WorkFlowInstance into the database, db sets returned sequence number into the wfi object.
wfiDAO.insert(wfi, authenticatedUser);
// Insert the TD0301Assignment into the db
tdas.setWorkflow_instance_id(wfi.getWorkflow_instance_id());
}
I'd like to remove the WorkflowInstance code out into its own method (still in this Class) like this:
if (isSucces()) {
insertWorkFlowInstance(request, tdas);
tdas.setWorkflow_instance_id(wfi.getWorkflow_instance_id());
but wfi is now marked by Eclipse as not available. Should I do something like this to fix the error so that I can still get the wfi.getWorkflow_instance_id() in the isSuccess block above? I know it removes the error, but I am trying to apply best practices.
public class TD0301AssignmentForm extends Form {
private WorkflowInstance wfi = new WorkflowInstance();
private WorkflowInstanceDAO wfiDAO = new WorkflowInstanceDAO();
Instance variables ("properties" or "fields") are not necessarily the way to go if they're not used throughout the entire class.
Variables should have the smallest scope possible--this makes code easier to reason about.
With some noise elided, and also guessing, it seems like the WorkflowInstance and WorkflowInstanceDao could be localized (names changed to match Java conventions):
public class TD0301AssignmentForm extends Form {
public Object insert(HttpServletRequest request) {
TD0301Assignment tdas = new TD0301Assignment();
try {
tdas.setCalcNum(FormUtil.getFieldValue(request, FIELD_CALC_NUM));
processDate(request, tdas);
tdas.setCalcDept(FormUtil.getFieldValue(request, FIELD_CALC_DEPT));
tdas.setYearOi(Integer.toString(DateUtil.getIntYear(dateNow)));
processCalcSafetyRequirements(request, tdas);
if (isSuccess()) {
WorkflowInstance wf = buildWorkflow(tdas);
tdas.setWorkflowInstanceId(wf.getId());
}
}
}
private buildWorkflow(TD0301Assignment tdas) {
WorkflowInstance wfi = new WorkflowInstance();
wfi.setWorkflowClassId(tdas.getCalcLevel());
wfi.setStartedBy(authenticatedUser.getBadge());
wfi.setStatus("0");
wfi.setLastDate(new Date());
WorkflowInstanceDao wfiDao = new WorkflowInstanceDao();
wfiDao.insert(wfi, authenticatedUser);
}
}
Whether or not this is appropriate depends on how/if the WorkflowInstance is used in the rest of the method snippet you show. The DAO is almost certainly able to be localized.
As methods become smaller and easier to think about, they become more testable.
For example, buildWorkflow is almost easy to test, except that the DAO is instantiated "manually". This means that testing the method will either (a) depend on having a working DAO layer, or (b) it must be mocked by a framework that can mock static utility methods (several can).
Without seeing all your code it's not easy to see exactlywhat you are trying to achieve. The reason eclipse is complaining is because it no longer has a wfi instance to play with because you've moved its local instance into your method, but creating another wfi instance is not likely to be your answer.
To get this working change the wfi to be class local and either use it's id directly or return wfi.getWorkflow_instance_id() from insertWorkFlowInstance() and then pass that value into tdas.setWorkflow_instance_id()
I have an array that I have created from a database ResultSet. I am trying to Serialize it so that I can send it over a socket stream. At the moment I am getting an error telling me that the array is not Serializable. The code I have is down below, the first part is the class to create an object for the array:
class ProteinData
{
private int ProteinKey;
public ProteinData(Integer ProteinKey)
{
this.ProteinKey = ProteinKey;
}
public Integer getProteinKey() {
return this.ProteinKey;
}
public void setProteinKey(Integer ProteinKey) {
this.ProteinKey = ProteinKey;
}
}
The code to populate the array:
public List<ProteinData> readJavaObject(String query, Connection con) throws Exception
{
PreparedStatement stmt = con.prepareStatement(query);
query_results = stmt.executeQuery();
while (query_results.next())
{
ProteinData pro = new ProteinData();
pro.setProteinKey(query_results.getInt("ProteinKey"));
tableData.add(pro);
}
query_results.close();
stmt.close();
return tableData;
}
And the code to call this is:
List dataList = (List) this.readJavaObject(query, con);
ObjectOutputStream output_stream = new ObjectOutputStream(socket.getOutputStream());
output_stream.writeObject(dataList);
And the code recieving this is:
List dataList = (List) input_stream.readObject();
Can someone help me serailize this array. All I can find in forums is simple arrays(EG. int[]).
I tried to add the serializable to the class and the UID number but got java.lang.ClassNotFoundException: socketserver.ProteinData error message. Does anyone now why?
Thanks for any help.
Basically you need that the classes you want to serialize are implementing Serializable. And if you want to avoid the warning related to the serial you should have also a long serialVersionUIDfor each one, that is a code used to distinguish your specific version of the class. Read a tutorial like this one to get additional info, serialization is not so hard to handle..
However remember that serialization is faulty when used between two different versions of the JVM (and it has some flaws in general).
Just a side note: the interface Serializabledoesn't actually give any required feature to the class itself (it's not a typical interface) and it is used just to distinguish between classes that are supposed to be sent over streams and all the others. Of course, if a class is Serializable, all the component it uses (instance variables) must be serializable too to be able to send the whole object.
Change your class declaration to:
class ProteinData implements Serializable {
...
}
I would have thought as a minimum that you would need
class ProteinData implements Serializable
and a
private static final long serialVersionUID = 1234556L;
(Eclipse will generate the magic number for you).
in the class.