I have a DAOImplementation class with the method definition below.
#Override
public Registration getRegistrationInfoById(int aRegistrationId) {
String SQL = "{CALL getRegistrationInfoById(?)}";
Registration aRegistration = new Registration();
try (Connection con = DBUtil.getConnection(DBType.MYSQL);
CallableStatement cs = con.prepareCall(SQL);) {
cs.setInt(1, aRegistrationId);
try (ResultSet rs = cs.executeQuery();) {
while (rs.next()) {
int gradeLevel = Integer.parseInt(rs.getString(RegistrationTable.GRADELEVEL));
aRegistration.setGradeLevel(gradeLevel);
}
}
} catch (SQLException e) {
JOptionPane.showMessageDialog(null, e.getErrorCode() + "\n" + e.getMessage());
}
return aRegistration;
}//end of method
This returns an integer value of Grade Level (1,2,3,4,5,6,7...so on...) which I've verified because I tried printing the output returned by aRegistration.getGradeLevel();
Now my problem is with my JComboBox. I have set a ListCellRenderer for my JComboBox which holds all the GradeLevel values
public class JComboBoxRenderer_GradeLevel extends JLabel implements ListCellRenderer<Object> {
public JComboBoxRenderer_GradeLevel() {
this.setOpaque(true);
}
#Override
public Component getListCellRendererComponent(JList<? extends Object> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
if (value instanceof GradeLevel) {
this.setText("" + ((GradeLevel) value).getGradelevel());
} else {
this.setText("--");
}
if (isSelected) {
this.setBackground(Color.YELLOW);
this.setForeground(list.getSelectionForeground());
} else {
this.setBackground(list.getBackground());
this.setForeground(list.getForeground());
}
return this;
}
}
And looks like this JComboBox as expected. (GradeLevel model is renderered to simply show an int value of gradelevel), ((GradeLevel) value).getGradelevel());returns an integer value.
I understand that even when JComboBox has its renderer that displays an integer value of GradeLevel by using ((GradeLevel)value).getGradeLevel(), the actual value on the JComboBox is still treated as instance of GradeLevel or object. But not a String or int.
So my problem is when I try to set the selected value to an int value, it won't change the selected value of the JComboBox. Nothing happens when I use setSelectedItem();
This is what I tried to do for the GUI.
//Grade Level
GradeLevelDaoImpl gldi = new GradeLevelDaoImpl();
List<GradeLevel> gradeLevels = gldi.getAllGradeLevelsInfo();
DefaultComboBoxModel gradeLevelModel = new DefaultComboBoxModel(gradeLevels.toArray());
jcmbGradeLevel.setModel(gradeLevelModel);
jcmbGradeLevel.setRenderer(new JComboBoxRenderer_GradeLevel());
jcmbGradeLevel.setSelectedIndex(-1);
GradeLevel gradeLevel = new GradeLevel();
gradeLevel.setGradelevel(registration.getGradeLevel());
jcmbGradeLevel.setSelectedItem(gradeLevel); //PROBLEM HERE, it doesn't change
JOptionPane displays this.
JOptionPane.showMessageDialog(null,"GradeLevel: "+gradeLevel);
JOptionPane.showMessageDialog(null,"GradeLevel: "+gradeLevel.getGradeLevel());
It doesn't seem to be able to compare the object I'm trying to set it to(gradeLevel) with the objects JComboBox has(gradeLevels). Notice the singular and plural.
How do I manipulate the types so that setSelectedItem() will match with what the JComboBox have?
Thanks.
If you want to do this by using different instances of the object, but with the same properties, then you need to override the class's equals and hashcode methods, so that the combination of properties are unique. This is very important, this is a relationship expectation that any object which is equal to another will have the same hashcode
This is a really quick example and I used by IDE's auto generation process (because I'm lazy), but, if your Registration class has other properties which should be considered when comparing to instances of the class, you will need to modify it to support them (again, any good IDE should be able to do this)
public class Registration {
private int gradeLevel;
public Registration(int gradeLevel) {
this.gradeLevel = gradeLevel;
}
public int getGradeLevel() {
return gradeLevel;
}
public void setGradeLevel(int gradeLevel) {
this.gradeLevel = gradeLevel;
}
#Override
public int hashCode() {
int hash = 7;
hash = 73 * hash + this.gradeLevel;
return hash;
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Registration other = (Registration) obj;
if (this.gradeLevel != other.gradeLevel) {
return false;
}
return true;
}
}
Then using something like...
Registration a = new Registration(1);
Registration b = new Registration(1);
Registration c = new Registration(2);
System.out.println(a.equals(b));
System.out.println(a.equals(c));
System.out.println(b.equals(c));
will print...
true
false
false
which shows us that the code is working.
Once you get this setup, you should then be able to change the selected item by creating an instance of Registration, seeding it with the required properties and passing it to the JComboBox.
This is very important and very common concept used a lot within Java, well worth taking the time to learn and understand
Related
I have Set of Object which has existing values and getting a new Set values as an update. If the new Set does contain the old Object then I do nothing, if the new Set contains new Object then I want send create update and if the new Set doesn't contain an existing object then I want send a delete update.
Object has two fields :
private PreferenceType preferenceType;
private String preferenceValue;
Currently I am comparing the existing Set objects against new Set objects and if I don't find any existing object in new set then sending the delete update.
private void sendDeletePreferenceMessage(Set<AccountPreference> existingAccountPreferences, Set<AccountPreference> accountPreferencesFromRequest) {
int counter = 0;
for(AccountPreference accountPreference : existingAccountPreferences) {
for(AccountPreference accountPreference1: accountPreferencesFromRequest) {
if(accountPreference.getPreferenceType().equals(accountPreference1.getPreferenceType()) &&
accountPreference.getPreferenceValue().equals(accountPreference1.getPreferenceValue()))
counter++;
}
if(counter == 0) {
accPrefDeleteSender.send(accountPreference);
}
}
}
And also comparing the new set of objects against existing set of Objects to find the new updates that I want send as a create update
private void sendCreatePreferenceMessage(Set<AccountPreference> accountPreferencesFromRequest, Set<AccountPreference> existingAccountPreferences) {
int counter = 0;
for(AccountPreference accountPreference : accountPreferencesFromRequest) {
for(AccountPreference accountPreference1: existingAccountPreferences) {
if(accountPreference.getPreferenceType().equals(accountPreference1.getPreferenceType()) &&
accountPreference.getPreferenceValue().equals(accountPreference1.getPreferenceValue()))
counter++;
}
if(counter == 0) {
accPrefCreateSender.send(accountPreference);
}
}
}
This works perfectly but I believe this could be simplified in a better way. Any suggestion of doing this better!
You are using two AccountPreference Objects: one with id field, and another without id. I don't know if it's possible without using interfaces or abstract classes. Nevertheless, according to your comments it seems to me you're confused how to override Object.equals method, so I'll give you an example:
class MyObjectWithId {
private long id;
private String someField;
public String getSomeField() {
return someField;
}
#Override
public boolean equals(Object other){
if (this == other) return true;
if (other == null || !other.getClass().isInstance(MyObject.class)) return false;
MyObject myObject = (MyObject) other;
return this.getSomeField().equals(myObject.getSomeField());
}
}
class MyObject {
private String someField;
public String getSomeField() {
return someField;
}
#Override
public boolean equals(Object other){
if (this == other) return true;
if (other == null || !other.getClass().isInstance(MyObjectWithId.class)) return false;
MyObjectWithId myObjectWithId = (MyObjectWithId) other;
return this.getSomeField().equals(myObjectWithId.getSomeField());
}
}
As you can see, you can ask if the two different types of Object are equal (in this case, there isn't need for overriding hashCode).
Now you should be able to do the rest (regarding your original question about two Sets)
Set has a method boolean contains(Object o).
You don't need two for loops, just iterate through one Set and check if another Set contains() the object!
You also need to override the boolean equals(Object o) in the POJO!
class MyObject {
private PreferenceType preferenceType;
private String preferenceValue;
#Override
public boolean equals(Object o){
if (this == o)
return true;
if (o == null || o.getClass()o.getClass() != getClass())
return false;
if(this.getPreferenceType().equals(o.getPreferenceType()) && this.getPreferenceValue().equals(o.getPreferenceValue()))
return true;
return false;
}
}
}
I have an Item object having 4 String fields and 3 boolean fields.
I have to construct this object based on the 3 boolean variables.
The target is whenever any one of the boolean variable is true we have to create the object having that/those boolean variable set.
If for any situation none of the boolean variables are true, we wont create the object.
I am using a COR to check whether any of the boolean fields will be set or not based on some business logic.
I was trying this with builder, but then I have to construct so many objects and later discard them when none of the boolean variables found true.
Can anyone have any better idea, to solve this kind of problem ?
Well thanks for the 2 delete flag for this question. Thank for the thoughts on this question as well.
I did something to achieve what I want. Which is quite flexible I believe. Only part if there is a dependency on If loop, but that is acceptable since Report class can have extra boolean so when that class is changed, it's builder should be touched to cater that change. Rest this is flexible which I wanted.
public class Report {
private String acftNo;
private Date plannedDate;
private String plannedStn;
private Integer mntncId;
private Set<String> capableStations;
private String routedStn;
private boolean isRoutedNEQPlannedStn; //Inconsistency type 1
private boolean isCapableAtPlannedStn; //Inconsistency type 2
private boolean isPlannedOrRoutedStationExists; //Inconsistency type 3/5
public Report(String acftNo, Integer mntncId) {
super();
this.acftNo = acftNo;
this.mntncId = mntncId;
}
public Report(String acftNo, Date plannedDate, String plannedStn,
Integer mntncId) {
super();
this.acftNo = acftNo;
this.plannedDate = plannedDate;
this.plannedStn = plannedStn;
this.mntncId = mntncId;
}
//setters and getters. Removed for space.
public static Report buildReport(Maintenance<?> task, Set<InconsistencyReport> enumSet) {
Report temp = new Report(task.getAssignment().getAircraftNumber(),task.getAssignment().getMntncScheduleDate(),
task.getAssignment().getStationCode(),task.getAssignment().getMntncId());
temp.setCapableStations(InconsistencyReport.getCapableStations(task));
for(InconsistencyReport ir : enumSet)
{
if(ir.compareTo(InconsistencyReport.ROUTED_STN_NEQ_PLANNED_STN)==0)
temp.setRoutedNEQPlannedStn(true);
if(ir.compareTo(InconsistencyReport.ITEM_NT_CAPABLE_AT_PLANNED_STN)==0)
temp.setCapableAtPlannedStn(true);
if(ir.compareTo(InconsistencyReport.NO_ROUTD_STN_ON_A_DATE)==0)
temp.setPlannedOrRoutedStationExists(true);
}
return temp;
}
}
calculateInconsitencyReport() method which will decide whether to create object or not.
public class InconsistencyReportChain {
public enum InconsistencyReport implements InconsistencyReportIface {
ROUTED_STN_NEQ_PLANNED_STN {
#Override
public boolean findInconsistency(Maintenance<?> task ) {
if(!validate(task))
return false;
//some logic
return true;
return false;
}
},
ITEM_NT_CAPABLE_AT_PLANNED_STN {
#Override
public boolean findInconsistency(Maintenance<?> task) {
if(!validate(task))
return false;
//some logic
return true;
return false;
}
},
NO_ROUTD_STN_ON_A_DATE {
#Override
public boolean findInconsistency(Maintenance<?> task) {
if(!validate(task))
return false;
//some logic
return true
return false;
}
};
#Override
public boolean validate(Maintenance<?> task) {
return !(null == task.getAssignment());
}
static Set<String> getCapableStations(Maintenance<?> task)
{
Set<String> capableStations = newHashSet();
if(task.getCapStationList() != null)
{
capableStations.addAll(Arrays.asList(task.getCapStationList().split(StringConstants.COMMA_SPLIT_REGEX)));
}
if(task.getCapStationClassList() != null)
{
Map<String, List<String>> stationClassMap = CacheManager.get(STN_CLASS.name());
List<String> stationClass = Arrays.asList(task.getCapStationClassList().split(StringConstants.COMMA_SPLIT_REGEX));
for(String stnClass : stationClass)
{
capableStations.addAll(stationClassMap.get(stnClass));
}
}
return capableStations;
}
}
public static Report calculateInconsitencyReport(Maintenance<?> task) {
Set<InconsistencyReport> enumSet = null;
for(InconsistencyReport iReport : InconsistencyReport.values())
{
if(iReport.findInconsistency(task))
{
if(null==enumSet)
enumSet = EnumSet.of(iReport);
else
enumSet.add(iReport);
}
}
if(null!= enumSet && enumSet.size() > 0)
return Report.buildReport(task,enumSet);
return null;
}
}
Helper Interface:
public interface InconsistencyReportIface {
public boolean findInconsistency(Maintenance<?> task );
public boolean validate(Maintenance<?> task );
}
Details of class logic is teared off because of security.
What is the problem? Just create your object when one of your booleans is true.
if(bool1 || bool2 || bool3) {
item = new Item(str1, str2, str3, str4, bool1, bool2, bool3);
}
From what I understand of your description:
a) you will have some bools that will determine wether you create a certain object or not.
b) you may have to include some more bools into the "check protocol"
c) you have to do this checking in a loop where
i/ you check for the bool variable
ii/ you check if the object had been created previously
I still don't quite get it yet, but.. that looks pretty straight forward to me. Let's say your bools are stored in a boolean array boolean[] bools and your strings in a string array String[] strings (which, btw, I don't know what they are used for). You are saying to check if every bool is true and then create an object based on that result.
boolean[] bools = new boolean[] { ... };
String[] strings = new String[] { ... };
boolean checks = false;
for(int i = 0; i<bools.length && !checks; i++)
checks = bools[i];
//so far we will have processed if any of the bools was false, which was your condition
if(checks)
Object object = new Object(); //create your desired object
I don't understand why you would need to check if the object has been constructed previously, though, so I didn't include it in my suggestion :P
I want to make a simple note reminder app that uses hashMap when the date is the key and the value is the text.
I have a Panel class(GUI), the hash-table class (Reminder.java), and a "MyDateClass.java" that represent a date for my purposes.
My gui is made of 3 JComboBox (day,month,year), One text area and 2 buttons - "Save", "Load".
The 2 buttons in the GUI Panel:
butSave.addActionListener(new ActionListener () {
public void actionPerformed(ActionEvent e) {
MyDateClass chosenDate = new MyDateClass(cbYear.getSelectedIndex()+2013,cbMonth.getSelectedIndex()+1, cb.getSelectedIndex()+1);
if(!remind.isReminderExists(chosenDate)){
remind.save(chosenDate, tfReminder.getText());
System.out.println("reminder doesnt exists");
}}
});
butLoad.addActionListener(new ActionListener () {
public void actionPerformed(ActionEvent e) {
System.out.println("tryin to load");
MyDateClass chosenDate = new MyDateClass(cbYear.getSelectedIndex()+2013,cbMonth.getSelectedIndex()+1, cb.getSelectedIndex()+1);
if(remind.isReminderExists(chosenDate)){
remind.Load(chosenDate);
System.out.println("reminder exists");
}}
});
Reminder class:
public class Reminder {
Map<MyDateClass,String> reminderMap;
public Reminder(){
reminderMap = new HashMap<MyDateClass,String>();
}
public boolean isReminderExists(MyDateClass date){
return reminderMap.containsKey(date);
}
public void save(MyDateClass date, String Input){
System.out.println("Trying to save");
reminderMap.put(date, Input);
}
public void Load(MyDateClass date){
System.out.println("Trying to load");
String output;
output = reminderMap.get(date);
System.out.println(output);
}
So after i push the save button i get from the console:
Trying to save
reminder doesnt exists
But then i push the Load button for the same date and
if(remind.isReminderExists(chosenDate))
Isnt triggerd.
What might be the problem?
Do i need to override hashCode() and equals() ? I genereted them but i dont if and how to change the equals() (do i need to manipulate it to compare both dates? How do i do that if "this" refers to the reminder Object)
Try something like this.
#Override public boolean equals(Object o) {
if(o == this) return true;
if(!(o instanceof MyDateClass)) return false;
MyDateClass that = (MyDateClass) o;
// use == for primitives
// use .compare for primitive wrappers where available
// use .equals for objects
return this.ivar1 == that.ivar2 &&
this.ivar2 == that.ivar2; //etc...
}
// equal objects must have equal hash codes
#Override public int hashCode() {
int result = 17;
result = 31 * result + ivar1;
result = 31 * result + ivar2;
return result;
}
Write JUnits to test for reflexive, symmetric, transitive, and consistent results.
As I know your problem clearly, I think that this function always return false. Because date(parameter) and the object in reminderMap have difference reference. So they can not be equal.
public boolean isReminderExists(MyDateClass date){
return reminderMap.containsKey(date);
}
If you want to use containsKey function of HashMap, maybe you should use String or Number instead of MyDateClass. I mean you should convert object of MyDateClass to String value before inserting it into reminderMap.
public boolean isReminderExists(String date){
return reminderMap.containsKey(date);
}
Otherwise, you can implement your code as Hot Licks mention.
I have an entity which has some BIT fields into the database:
editable
needs_review
active
These fields are mapped against boolean fields in its Java class using Hibernate 3.6.9 version. That forces me to write an interface method for each List of entities I want to get:
List<Entity> listEditables();
List<Entity> listReviewNeeded();
List<Entity> listActives();
Or write a general interface method to achieve a combination of them:
List<Entity> listEntities(boolean editables, boolean reviewNeeded, boolean actives);
That second choice looks greater, but if I add another field in the future there will be a need to modify the interface itself (and every line of code coupled to it).
So I decided I can express it as an enumeration Set:
public enum EntityType{
EDITABLE, REVIEW_NEEDED, ACTIVE
}
//That way there's no need to change interface method's signature
List<Entity> listEntities(Set<EntityType> requiredTypes);
It makes sense that being an enumeration match what I want to achieve, the Entity type itself should have its own Set<EntityType>:
public class Entity{
Set<EntityType> entityTypes;
}
However instead of that I have the mapped booleans which logically match that Set. Then my question, is there any way to map Set<EntityType> entityTypes in hibernate based in that BIT fields or do I have to manage that logic myself having them as boolean?
UPDATE
Having them mapped as a Set implies the possibility of querying for a List using an in clause, if not it would imply an extra step for conversion between my controller and model codes.
Set<EntityType> typesSet = Sets.newHashSet(EntityType.EDITABLE, EntityType.REVIEW_NEEDED);
//Obtains a list of every single entity which is EDITABLE or REVIEW_NEEDED
session.createCriteria(Entity.class).addRestriction(Restrictions.in("entityTypes",typeSet)).list();
I think I have a solution for you. What you are interested in is a CompositeUserType.
As an example lets use a InetAddress composite user type I wrote lately to map a 128bit IPv6 Address / IPv4Address object to two 64bit long properties inside a user account entity.
The signupIp:InetAddress is mapped towards two columns (there is no column count limit or alike) using:
#Columns(columns = {#Column(name = "ip_low", nullable = true), #Column(name = "ip_high", nullable = true)})
private InetAddress signupIp;
And the interesting part of the implementation looks like this:
public class InetAddressUserType implements CompositeUserType {
#Override
public String[] getPropertyNames() {
return new String [] {"ipLow", "ipHigh"};
}
#Override
public Type[] getPropertyTypes() {
return new Type [] { LongType.INSTANCE, LongType.INSTANCE};
}
#Override
public Object getPropertyValue(Object component, int property) throws HibernateException {
if(component != null)
return toLong((InetAddress)component)[property];
else
return null;
}
#Override
public void nullSafeSet(PreparedStatement st, Object value, int index,
SessionImplementor session) throws HibernateException, SQLException {
if(value != null) {
long [] longs = toLong((InetAddress)value);
st.setLong(index, longs[0]);
st.setLong(index + 1, longs[1]);
}
else {
st.setNull(index, LongType.INSTANCE.sqlType());
st.setNull(index + 1, LongType.INSTANCE.sqlType());
}
}
#Override
public void setPropertyValue(Object component, int property, Object value)
throws HibernateException {
throw new RuntimeException("This object is immutable");
}
#Override
public Class<?> returnedClass() {
return InetAddress.class;
}
#Override
public boolean equals(Object x, Object y) throws HibernateException {
return x != null ? x.equals(y) : null == y;
}
#Override
public int hashCode(Object x) throws HibernateException {
return x.hashCode();
}
#Override
public Object nullSafeGet(ResultSet rs, String[] names,
SessionImplementor session, Object owner)
throws HibernateException, SQLException {
Long ipLow = rs.getLong(names[0]);
if(!rs.wasNull()) {
Long ipHigh = rs.getLong(names[1]);
try {
return fromLong(new long [] {ipLow, ipHigh});
} catch (UnknownHostException e) {
throw new HibernateException("Failed to get InetAddress: ip = " + ipHigh + " + " + ipLow, e);
}
}
else
return null;
}
#Override
public Object deepCopy(Object value) throws HibernateException {
if(value != null)
try {
return InetAddress.getByAddress(((InetAddress)value).getAddress());
} catch (UnknownHostException e) {
throw new RuntimeException("Impossible Exception: " + e.getMessage(), e);
}
else
return null;
}
#Override
public boolean isMutable() {
return false;
}
...
}
Note that I flexibly switch between Inet4Address and Inet6Address instances depending on the values of ipLow and ipHigh. The composite is marked as immutable and you need to check the documentation and the examples in the Hibernate source code (build in composite user types).
In a similar way you can map your meaningful bit properties. You can query those bits by using a single Restriction.eq refering to your EnumType. You can use the equals method to check the properties object. And if you need to refer to a special mapped bit you can use the dot notation like in signupIp.ipLow to refer to the ipLow property/column.
I guess this is what you are looking for.
Update:
In the end it boils down to define the right order of your properties. Hibernate will always use integer index values to access each property:
//immutable for simplicity
class Status {
private final boolean editable;
private final boolean needsReview;
private final boolean active;
//... constructor + isEditable etc..
}
In your StatusCompositeType class:
public String[] getPropertyNames() {
return new String [] {"editable", "needsReview", "active"};
}
public Type[] getPropertyTypes() {
return new Type [] { BooleanType.INSTANCE, LongType.INSTANCE};
}
public Object getPropertyValue(Object component, int property) throws HibernateException {
if(component != null) {
Status status = (Status)component;
switch(property) {
case 1: return status.isEditable();
case 2: return status.isReviewNeeded();
case 3: return status.isActive();
default: throw new IllegalArgumentException();
}
}
else
return null; //all columns can be set to null if you allow a entity to have a null status.
}
public void nullSafeSet(PreparedStatement st, Object value, int index,
SessionImplementor session) throws HibernateException, SQLException {
if(value != null) {
Status status = (Status)value;
st.setBoolean(index, status.isEditable());
st.setBoolean(index + 1, status.isReviewNeeded());
st.setBoolean(index + 2, status.isActive());
}
else {
st.setNull(index, BooleanType.INSTANCE.sqlType());
st.setNull(index + 1, BooleanType.INSTANCE.sqlType());
st.setNull(index + 2, BooleanType.INSTANCE.sqlType());
}
}
public Object nullSafeGet(ResultSet rs, String[] names,
SessionImplementor session, Object owner)
throws HibernateException, SQLException {
Boolean isEditable = rs.getBoolean(names[0]);
if(!rs.wasNull()) {
Boolean isReviewNeeded = rs.getBoolean(names[1]);
Boolean isActive = rs.getBoolean(names[2]);
return new Status(isEditable, isReviewNeeded, isActive);
}
else
return null;
}
The rest is straight forward. Remember to implement equals and hashcode for the user type and add the type to the configuration before you create your sessionFactory.
Once you have everything in place you can create a criteria search and use:
//search for any elements that have a status of editable, no reviewNeeded and is not active (true false false).
criteria.add(Restrictions.eq("status", new Status(true, false, false));
Now your listEntities method may become either: listEntities(Status status) or listEntities(boolean editable, boolean reviewNeeded, boolean isActive).
If you need additional information just check the CompositeType and BasicType implementations Hibernate provides within its own sourcecode (look for implementors of CompositeType and BasicType). Understanding those helps alot to use and learn this intermediate level knowledge of Hibernate.
After some brainstorming, I've gone to a workaround which I consider the second best one being imposible to map an enum for the booleans in Hibernate. This is how I have my Entity class looks now:
public class Entity{
private boolean editable;
private boolean needsReview;
private boolean active;
//getters and setters
}
My listing method is implemented as this:
public List<Entity> listEntities(Set<EntityType> requiredTypes){
Criteria cri = session.createCriteria(Entity.class);
if (requiredTypes.contains(EntityType.EDITABLE)){
cri.addRestriction(Restrictions.eq("editable",true));
}
if (requiredTypes.contains(EntityType.NEEDS_REVIEW)){
cri.addRestriction(Restrictions.eq("needsReview",true));
}
if (requiredTypes.contains(EntityType.ACTIVE)){
cri.addRestriction(Restrictions.eq("active",true));
}
return cri.list();
}
Not bad, but don't know if it's the only way to go with that!
I don't think hibernate provides a way to manage the mappings the way you're describing. You can create your own UserType (https://community.jboss.org/wiki/Java5EnumUserType) but every time you add a new enum value you will have to change the logic in the UserType to map the new field as well.
The alternative will be to convert this into a one to many relationship. Your point is basically that if you want to add more fields you will have to change the signature of listEntities but also you will have to modify your table.
So, instead you can create a table that will contain your entity types and have a #OneToMany` relationship to it from your entity. For example:
Define your flags as required:
public enum Flags {
EDITABLE, REVIEW_NEEDED, ACTIVE
}
Create a one-to-many relationship to EntityType:
#Entity
#Table( name="entity" )
public class Entity implements Serializable {
#OneToMany(mappedBy = "entity")
public Set<EntityType> getEntityTypes() {
return entityTypes;
}
And a many-to-one to Entity:
#Entity
#Table( name="entityType" )
public class EntityType implements Serializable {
#Id
private Integer id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ENTITY_ID")
private Entity entity;
#Enumerated(EnumType.STRING)
private Flag entityType;
...
}
PD: Please note the code is just an example and is not complete or tested.
I'm now looking into JTables and have a bunch of business objects that I retrieve from the DB with Hibernate + Spring Data JPA.
I love that Spring Data JPA handles all the cumbersome implementation of the DAL and was wondering if there's something similar for TableModel.
Basically, I would have something along the lines of:
public class GenericTableModel<T> extends AbstractTableModel
And the GenericTableModel would use reflection and/or annotations to look into T.
Does something like this exist? I hope I don't have to have a TableModel for each object I want to display on a JTable..
And the GenericTableModel would use reflection to look into T.
The Bean Table Model does this.
http://java.net/projects/beansbinding/
Will let you bind business objects to view components (including tables) as long as you have Bean-Style getters and setters.
I am in the process of writing a library for Swing Developers. Its a work in progress. But i have done what you are looking for. Take a look at the classes in this package:
http://code.google.com/p/swingobjects/source/browse/#git%2FSwingObjects%2Fsrc%2Forg%2Faesthete%2Fswingobjects%2Fview%2Ftable
For an example of how to use this, please take a look at this class- Look at line numbers - 70-85
http://code.google.com/p/swingobjects/source/browse/SwingObjects/src/test/CompTest.java
I havent got the documentation written yet. But if you dont follow anything, please comment back here.
Update - Code sample
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import org.aesthete.swingobjects.annotations.Column;
import org.aesthete.swingobjects.view.table.RowDataBean;
import org.aesthete.swingobjects.view.table.SwingObjTable;
import org.jdesktop.swingx.JXFrame;
public class TableDemo {
public static void main(String[] args) {
try {
//For this demo the Framework need not be initialised.. If you plan on using the entire framework, then
//its best you initialise it before working on anything...
// SwingObjectsInit.init("/swingobjects.properties", "/error.properties");
//Here's the data to show on the table
final List<Row> rows = new ArrayList<Row>();
rows.add(new Row("Data 1", "Data 2", "Yes", true));
rows.add(new Row("Data 3", "Data 4", "No", false));
//Create the swing table as below.. Provide the Row.class to say that the data in the rows
// will be from this class
final SwingObjTable<Row> table = new SwingObjTable<Row>(Row.class);
table.setData(rows);
table.setVisibleRowCount(4);
//Make any column into a combo box by calling the below method.
//A column can be automatically made into a checkbox, by defining your property in the Row class as a boolean
table.makeColumnsIntoComboBox(new String[] { "Yes", "No" }, 2);
//Initialise the frame and show it on the screen
final JXFrame frame = new JXFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(new JScrollPane(table));
frame.pack();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
public static class Row extends RowDataBean{
#Column(index=0,name="Column 1",editable=true)
private String column1;
#Column(index=1,name="Column 2",editable=true)
private String column2;
#Column(index=2,name="Column 3",editable=true)
private String column3;
#Column(index=3,name="Column 4",editable=true)
private boolean column4;
public Row(String column1, String column2, String column3, boolean column4) {
super();
this.column1 = column1;
this.column2 = column2;
this.column3 = column3;
this.column4 = column4;
}
public String getColumn1() {
return column1;
}
public void setColumn1(String column1) {
this.column1 = column1;
}
public String getColumn2() {
return column2;
}
public void setColumn2(String column2) {
this.column2 = column2;
}
public String getColumn3() {
return column3;
}
public void setColumn3(String column3) {
this.column3 = column3;
}
public boolean getColumn4() {
return column4;
}
public void setColumn4(boolean column4) {
this.column4 = column4;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((column1 == null) ? 0 : column1.hashCode());
result = prime * result + ((column2 == null) ? 0 : column2.hashCode());
result = prime * result + ((column3 == null) ? 0 : column3.hashCode());
result = prime * result + (column4 ? 1231 : 1237);
return result;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Row other = (Row) obj;
if (column1 == null) {
if (other.column1 != null)
return false;
} else if (!column1.equals(other.column1))
return false;
if (column2 == null) {
if (other.column2 != null)
return false;
} else if (!column2.equals(other.column2))
return false;
if (column3 == null) {
if (other.column3 != null)
return false;
} else if (!column3.equals(other.column3))
return false;
if (column4 != other.column4)
return false;
return true;
}
}
}
Update: Answer to queries in Comments
1) The table can be inserted in any Component, right? (JPanel, JScrollPane, etc.),
Yes. The table extends JXTable (swingx) which extends JTable. So it can be placed inside any component. You would ideally place into a JScrollPane though.
2) We have control over the column names? (my app is localized in several langs)
Thanks for this. I didnt think about localisation when I started making the framework. You pointed me in the right direction. I was able to modify the framework quickly to achieve this. Its easy actually:
To make use of l10n - Before you start coding, make sure you initialize the Swing Objects Framework with the below line. If you dont provide a Locale, the default Locale will be applied.
SwingObjectsInit.init("swingobjects", "application",new Locale("fr", "FR"));
You need to have to have 2 sets of properties file.
1) swingobjects - This provides the defaults for the swingobjects framework. The file is already provided in my code base. Just copy paste the file onto a classpath location.
2) application - This is where you will need to put in your application's GUI texts/messages. You will have to make a new application properties file for every Locale you need.
Then finally, instead of saying
#Column(index=0,name="Column 1",editable=true)
private String column1;
You will have to use:
#Column(index=0,key="test.column1",editable=true)
private String column1;
Change name to key. This will make it read the Resource Bundle and search for a property test.column1 rather than your hard coded column Name.
3) Does SwingObjTable require hashCode and equals to be implemented?
The equals and hashcode is required if you use the Swing Objects Framework in its entirety. The Swing Objects Framework, allows you to set data for the GUI elements in a bean in your model class - Read as in MVC. The framework, then automatically updates the GUI if the bean's value has changed. In order to check if the bean's value has indeed changed, it needs to call the equals method. Hence you need to override it. You can make Eclipse generate this for you. Or if you dont use anything in the framework, then just call super.equals() from there.
Just check out / clone the git repo and you'll have be access the TableDemo example..
git clone https://writetosethu#code.google.com/p/swingobjects/