Now I'm using play2.2.1, and I know Java, but I don't know Scala.
And if you learn PlayFramework even just a little bit, you will know Scala is used in some extent in the framework. This is just a background about me. The question is why is Tuple2 used in below code although the number of passing values is one not plural.
This is the controller's code:
//Action for making a message
public static Result add() {
Form<Message> f = new Form(Message.class);
List<Member> mems = Member.find.select("name").findList();
List<Tuple2<String, String>> opts =
new ArrayList<Tuple2<String, String>>();
for(Member mem: mems) {
opts.add(new Tuple2(mem.name, mem.name));
}
return ok(add.render("fill-in form", f, opts));
}
This is the Model's code:
#Entity
public class Member extends Model {
#OneToMany(cascade = CascadeType.ALL)
public List<Message> messages = new ArrayList<Message>();
#Id
public Long id;
#Required(message = "This item is Required")
public String name;
#Email(message = "Please Fill in a message")
public String mail;
public String tel;
public static Finder<Long, Member> find = new Finder<Long, Member>(Long.class, Member.class);
#Override
public String toString() {
String ids = "{id:";
for (Message m: messages) {
ids += " " + m.id;
}
ids += "}";
return ("[id:" + id + ", message:" + ids + ", name:" + name + ", mail:" + mail + ", tel:" + tel + "]");
}
public static Member findByName(String input) {
return Member.find.where().eq("name", input).findList().get(0);
}
}
This is the View's code:
#(msg:String, form1: Form[models.Message], opts: List[Tuple2[String, String]])
#main("Sample page") {
<h1>ADD MESSAGE</h1>
<p>#msg</p>
#helper.form(action = routes.Application.create) {
#(helper.select (
field = form1("name"),
options = opts
))
#(helper.textarea (
field = form1("message")
))
<input type="submit">
}
}
Again, why is Tuple2, which contains two String values, used despite one value passed from the controller to the view?
If the information I showed here is not enough, please give me a comment.
The role of a tuple is to provide multiple values in a single instance. Perhaps it's a bit like an unnamed class? To summarise the comments and give a clear example, the structure of the <option> tag in a <select> structure is
<option value="internal-value">shown-value</option>
We use it often for foreign key situations - the foreign key value is in "internal-value" and the name or other attribute from the linked table is in the shown-value.
If you use the play helper #select to generate it, you should provide a Seq of tuple2s - i.e. two strings for each option. The first for the internal value, and the second for the displayed value. I use a defining but that is just one way to do it. For example:
#defining(for (a <- AppType.all()) yield (a.id.toString,a.name)) { appTypeList =>
#select(
apiForm("forApp"),
appTypeList,
'class -> "form-control",
'_default -> "-- None Selected --",
'_label -> "App Type"
)
}
You can generate it yourself manually if you have some special needs or reason not to use the helper (or you can write your own helper :) ). For example:
<select name="forApiType" id="forApiType">
#ApiType.all().map { a =>
<option value="#a.id" #if(a.id==apiType.id) {selected}>
#a.name
</option>
}
</select>
Related
I have something like this in drl file:
import java.lang.String
global String result;
rule ''Rule 1'' when some condition
then
result = "PASS";
kcontext.getKnowledgeRuntime().setGlobal("Result", result); // I got an "Unexpected global" exception.
System.out.println("result = "+ Result);
Also, I don't know how to access this global variable from my MyService.java class.
I was trying to set a global variable from the drl file not my java class like Service class.
All I had to do was the following and it worked successfully
import java.lang.String
global String result;
rule ''Rule 1''
when
some condition
then
String grade = "PASS";
kcontext.getKnowledgeRuntime().setGlobal("result", grade);
end
Also, the global variable name should match what I pass on the setGlobal("result",...).
And then get the global variable using the session I have in the Service class. like:
session.getGlobal("result");
Your rule should not be touching the 'kcontext'. What in the world are you trying to do? result = "PASS" is sufficient for setting the value of the global.
global String result
rule "Rule 1"
when
// some condition
then
result = "PASS";
end
Of course it's not going to work like you want it to because you need to change the value of the existing object; you can't overwrite it like that. Some options might be a "ResultsHolder" sort of class with a boolean variable you can set; or maybe even an AtomicBoolean that you can call set on.
To fire rules with a global, you need to add the global objects to the KieBase before invoking your rules:
var value = ...; // some OBJECT which you are going to pass in as a global
KieSession session = ruleBase.newStatefulSession();
session.insert(...); // insert data
session.setGlobal( "myGlobalFoo", value ); // sets the global; note the name must match the rule file!
session.fireAllRules();
After the rules are fired, you'll have your reference to value that you can use. This is also why you can't pass strings as globals and expect them to capture changes -- Java is pass-by-value, not pass-by-reference.
Here's an example for passing results out of the rules. This toy app will check the student's score on a test and then decide if they passed or failed.
Classes:
class Student {
private String name;
public String getName() { return this.name; }
public void setName(String name) { this.name = name; }
}
class Exam {
private String name;
private Double score;
public String getName() { return this.name; }
public void setName(String name) { this.name = name; }
public Double getScore() { return this.score; }
public void setScore(String score) { this.score = score; }
}
class ExamResults {
private List<String> results = new ArrayList<>();
public void logResults( String name, Double score, boolean passed ) {
this.results.add(name + " scored " + score + "%, which is a " + (passed ? "passing": "failing") + " grade.");
}
public List<String> getResults() { return this.results; }
}
Rule:
global ExamResults results;
rule "Evaluate exam"
when
Student( $name: name )
Exam ( $score: score, name == $name )
then
boolean passed = $score > 60.0;
results.logResults( $name, $score, passed );
end
Invocation:
List<Student> students = ...;
List<Exam> exams = ... ;
ExamResults results = new ExamResults();
KieSession session = ruleBase.newStatefulSession();
students.forEach( student -> session.insert(students) );
exams.forEach( exam -> session.insert(exam) );
session.setGlobal( "results", results);
session.fireAllRules();
// Print the results:
results.getResults().forEach(System.out::println);
If all you're trying to do is to get some data out of your rules (eg whether certain conditions match), I've written up answers about how to do that previously here and here. If you just want to know what rules triggered, you should write a listener which logs rule hits ("afterMatchFired").
Thank you for help :)
I tried to get last id, and read many post about it, but i don't arrive to apply it in my case.
First Class
private Date date;
private List<AdsEntity> adsDetails;
... getters and setters
Second Class (AdsEntity)
private int id;
private String description;
There is the code where i try to get the last id :
Mapper
#Insert({
"<script>",
"INSERT INTO tb_ads_details (idMyInfo, adDate)"
+ " VALUES"
+ " <foreach item='adsDetails' index='index' collection='adsDetails' separator=',' statement='SELECT LAST_INSERT_ID()' keyProperty='id' order='AFTER' resultType='java.lang.Integer'>"
+ " (#{adsDetails.description, jdbcType=INTEGER}) "
+ " </foreach> ",
"</script>"})
void saveAdsDetails(#Param("adsDetails") List<AdsDetailsEntity> adsDetails);
In debugging mode, when I watch List I see the id still at 0 and don't get any id.
So what I wrote didn't workout :(
Solution Tried with the answer from #Roman Konoval :
#Roman Konoval
I apply what you said, and the table is fully well set :)
Just one problem still, the ID is not fulfill
#Insert("INSERT INTO tb_ads_details SET `idMyInfo` = #{adsDetail.idMyInfo, jdbcType=INTEGER}, `adDate` = #{adsDetail.adDate, jdbcType=DATE}")
#SelectKey(statement = "SELECT LAST_INSERT_ID()", before = false, keyColumn = "id", keyProperty = "id", resultType = Integer.class )
void saveAdsDetails(#Param("adsDetail") AdsDetailsEntity adsDetail);
default void saveManyAdsDetails(#Param("adsDetails") List<AdsDetailsEntity> adsDetails)
{
for(AdsDetailsEntity adsDetail:adsDetails) {
saveAdsDetails(adsDetail);
}
}
Thank for your help :)
Solution add to #Roman Konoval proposal from #Chris advice
#Chris and #Roman Konoval
#Insert("INSERT INTO tb_ads_details SET `idMyInfo` = #{adsDetail.idMyInfo, jdbcType=INTEGER}, `adDate` = #{adsDetail.adDate, jdbcType=DATE}")
#SelectKey(statement = "SELECT LAST_INSERT_ID()", before = false, keyColumn = "id", keyProperty = "adsDetail.id", resultType = int.class )
void saveAdsDetails(#Param("adsDetail") AdsDetailsEntity adsDetail);
default void saveManyAdsDetails(#Param("adsDetails") List<AdsDetailsEntity> adsDetails)
{
for(AdsDetailsEntity adsDetail:adsDetails) {
saveAdsDetails(adsDetail);
}
}
Thanks to all of you, for the 3 suggestions!!!
yes. it doesnt work.
please take a look at mapper.dtd
foreach-tag doesnt support/provide the following properties statement, keyProperty order and resultType
if you need the id for each inserted item please let your DataAccessObject handle iteration and use something like this in your MapperInterface
#Insert("INSERT INTO tb_ads_details (idMyInfo, adDate) (#{adsDetail.idMyInfo, jdbcType=INTEGER}, #{adsDetail.adDate, jdbcType=DATE})")
#SelectKey(before = false, keyColumn = "ID", keyProperty = "id", resultType = Integer.class, statement = { "SELECT LAST_INSERT_ID()" } )
void saveAdsDetails(#Param("adsDetail") AdsDetailsEntity adsDetail);
please ensure AdsDetailsEntity-Class provides the properties idMyInfoand adDate
Edit 2019-08-21 07:25
some explanation
referring to the mentioned dtd the <selectKey>-tag is only allowed as direct child of <insert> and <update>. it refers to a single Object that is passed into the mapper-method and declared as parameterType.
its only executed once and its order property tells myBatis wether to execute it before or after the insert/update statement.
in your case, the <script> creates one single statement that is send to and handled by the database.
it is allowed to combine #Insert with <script> and <foreach> inside and #SelectKey. but myBatis doesnt intercept/observe/watch database handling the given statement. and as mentioned before, #SelectKey gets executed only once, before or after #Insert-execution. so in your particular case #SelectKey returns the id of the very last inserted element. if your script inserts ten elements, only the new generated id of tenth element will be returned. but #SelectKey requires a class-property with getter and setter to put the selected id into - which List<?> doesnt provide.
example
lets say you want to save an Advertisement and its AdvertisementDetails
Advertisement has an id, a date and details
public class Advertisement {
private List<AdvertisementDetail> adDetails;
private Date date;
private int id;
public Advertisement() {
super();
}
// getters and setters
}
AdvertisementDetail has its own id, a description and an id the Advertisementit belongs to
public class AdvertisementDetail {
private String description;
private int id;
private int idAdvertisement;
public AdvertisementDetail() {
super();
}
// getters and setters
}
the MyBatis-mapper could look like this. #Param is not used, so the properties are accessed direct.
#Mapper
public interface AdvertisementMapper {
#Insert("INSERT INTO tb_ads (date) (#{date, jdbcType=DATE})")
#SelectKey(
before = false,
keyColumn = "ID",
keyProperty = "id",
resultType = Integer.class,
statement = { "SELECT LAST_INSERT_ID()" })
void insertAdvertisement(
Advertisement ad);
#Insert("INSERT INTO tb_ads_details (idAdvertisement, description) (#{idAdvertisement, jdbcType=INTEGER}, #{description, jdbcType=VARCHAR})")
#SelectKey(
before = false,
keyColumn = "ID",
keyProperty = "id",
resultType = Integer.class,
statement = { "SELECT LAST_INSERT_ID()" })
void insertAdvertisementDetail(
AdvertisementDetail adDetail);
}
the DataAccessObject (DAO) could look like this
#Component
public class DAOAdvertisement {
#Autowired
private SqlSessionFactory sqlSessionFactory;
public DAOAdvertisement() {
super();
}
public void save(
final Advertisement advertisement) {
try (SqlSession session = this.sqlSessionFactory.openSession(false)) {
final AdvertisementMapper mapper = session.getMapper(AdvertisementMapper.class);
// insert the advertisement (if you have to)
// its new generated id is received via #SelectKey
mapper.insertAdvertisement(advertisement);
for (final AdvertisementDetail adDetail : advertisement.getAdDetails()) {
// set new generated advertisement-id
adDetail.setIdAdvertisement(advertisement.getId());
// insert adDetail
// its new generated id is received via #SelectKey
mapper.insertAdvertisementDetail(adDetail);
}
session.commit();
} catch (final PersistenceException e) {
e.printStackTrace();
}
}
}
What Chris wrote about inability to get ids in the foreach is correct. However there is a way to implement id fetching in mapper without the need to do it externally. This may be helpful if you use say spring and don't have a separate DAO layer and your mybatis mappers are the Repository.
You can use default interface method (see another tutorial about them) to insert the list of items by invoking a mapper method for single item insert and single item insert method does the id selection itself:
interface ItemMapper {
#Insert({"insert into myitem (item_column1, item_column2, ...)"})
#SelectKey(before = false, keyColumn = "ID",
keyProperty = "id", resultType = Integer.class,
statement = { "SELECT LAST_INSERT_ID()" } )
void saveItem(#Param("item") Item item);
default void saveItems(#Param("items") List<Item> items) {
for(Item item:items) {
saveItem(item);
}
}
MyBatis can assign generated keys to the list parameter if your DB/driver supports multiple generated keys via java.sql.Statement#getGeneratedKeys() (MS SQL Server, for example, does not support it, ATM).
The following example is tested with MySQL 5.7.27 + Connector/J 8.0.17 (you should include version info in the question).
Be sure to use the latest version of MyBatis (=3.5.2) as there have been several spec changes and bug fixes recently.
Table definition:
CREATE TABLE le tb_ads_details (
id INT PRIMARY KEY AUTO_INCREMENT,
description VARCHAR(32)
)
POJO:
private class AdsDetailsEntity {
private int id;
private String description;
// getters/setters
}
Mapper method:
#Insert({
"<script>",
"INSERT INTO tb_ads_details (description) VALUES",
"<foreach item='detail' collection='adsDetails' separator=','>",
" (#{detail.description})",
"</foreach>",
"</script>"
})
#Options(useGeneratedKeys = true, keyProperty="adsDetails.id", keyColumn="id")
void saveAdsDetails(#Param("adsDetails") List<AdsDetailsEntity> adsDetails);
Note: You should use batch insert (with ExecutorType.BATCH) instead of multi-row insert (=<foreach/>) when inserting a lot of rows.
This issue arrived when I tried to use a LinkedHashMap to save entries from a database. I don't know to what extent the xhtml code is working, but superficially looks well.
The issue is in this part of the xhtml:
<tbody>
<tr>
<td>Movie:</td>
<!--td>
<h:selectOneMenu id="foundmovieid" value="#{webRental.idmovie}">
<f:selectItems id="movieid" value="#{webMovie.get_all_movies()}"></f:selectItems>
</h:selectOneMenu>
</td-->
<td>
<h:selectOneMenu value="#{webRental.idmovie}">
<f:selectItems value="#{webMovie.availableMovies.entrySet()}" var="entry"
itemValue="#{entry.key}" itemLabel="#{entry.value}" />
</h:selectOneMenu>
</td>
</tr>
</tbody>
</table>
<h:commandButton value="Submit" action="#{webRental.save()}"></h:commandButton>
Here is the WebRental.java:
public class WebRental {
#EJB
private RentalsFacade rf;
private String iduser;
private String idmovie;
//getters and setters
public List<Rentals> get_all_rents() {
return rf.findAll();
}
public void save() {
try {
rf.Save(iduser, idmovie);
} catch (Exception e) {
}
}
}
and the WebMovie.java, whose MoviesFacade has an EntityManager and a way to persist new pbject Movies (id, title, director's name and length):
#Named
#RequestScoped
public class WebMovie {
#EJB
private MoviesFacade mf;
private String id;
private String title;
private String director;
private int length;
private Map<String, String> availableMovies;
//geters and setters
public List<Movies> get_all_movies() {
availableMovies = new LinkedHashMap<>();
List<Movies> found = mf.findAll();
found.forEach((m) -> {
String first = m.getId();
String second = m.getTitle() + ", " + m.getDirector() + ", " + m.getLength() + " minutes.";
availableMovies.put(first,second);
});
return mf.findAll();
}
public void save() {
try {
mf.Save(id, title, director, length);
} catch (Exception e) {
}
}
}
In the xhtml, theres a muted section, which is just what I have to do (get id's and submit them) but in a way that you only see the id's. The unmuted section is the part im having trouble with, since it says that the value is invalid.
To solve this do I have to use "converters"? If so, how do I implement it? If not, what is my error?
Also, in the muted section there is a call to "get_all_movies()", but since it's muted, it shouldnt be called. How do I call that function outside of that muted section so I can delete the whole section after I get the SelectOneMenu with the map working?
Turns out using Maps is very strange...
How I solved this was by removing most of the SelectItems so that it just is:
<f:selectItems value="#{webMovie.availableMovies}" />
And then changing the way I insert everything to the Map like so:
#PostConstruct
public void init() {
availableMovies = new HashMap<>();
mf.findAll().forEach((m) -> {
availableMovies.put((String) m.getTitle() + ", " + m.getDirector() + ", " + m.getLength() + " minutes.",(String) m.getId());
});
//Turns out that, for the selectItems to read properly the map, you have to insert the text you want to be displayed as the key, and then the value as value. I was doing it backwards.
}
That way I dont have to depend on the !--'d section that calls get_all_movies() and I could delete it!
OK, i need someone to explain to me where to start on this project.
First I need to overload the constructor by adding a default (no-args) constructor to Person that defines an object to have the name "N/A" and an id of -1.
Then i need to add a setter method named reset that can be used to reset the two private instance variables of this class to two values passed in as parameters.
Then I need to add a getter method named getName and getId that can be used to retrieve these two private variables
Here is the code:
public class Person
{
private String name;
private int id;
private static int personCount = 0;
// constructor
public Person(String pname)
{
name = pname;
personCount++;
id = 100 + personCount;
}
public String toString()
{
return "name: " + name + " id: " + id
+ " (Person count: " + personCount + ")";
}
// static/class method
public static int getCount()
{
return personCount;
}
////////////////////////////////////////////////
public class StaticTest
{
public static void main(String args[])
{
Person tom = new Person("Tom Jones");
System.out.println("Person.getCount(): " + Person.getCount());
System.out.println(tom);
System.out.println();
Person sue = new Person("Susan Top");
System.out.println("Person.getCount(): " + Person.getCount());
System.out.println(sue);
System.out.println("sue.getCount(): " + sue.getCount());
System.out.println();
Person fred = new Person("Fred Shoe");
System.out.println("Person.getCount(): " + Person.getCount());
System.out.println(fred);
System.out.println();
System.out.println("tom.getCount(): " + tom.getCount());
System.out.println("sue.getCount(): " + sue.getCount());
System.out.println("fred.getCount(): " + fred.getCount());
}
}
I'm not exactly sure where to start and I don't want just the answer. I'm looking for someone to explain this clearly.
First I need to overload the constructor by adding a default (no-args) constructor to Person that defines an object to have the name "N/A" and an id of -1.
Read about constructors here.
The Person class already contains a ctor that takes 1 argument. What you need to do is create a "default ctor" which is typically a ctor w/out any parameters.
Example:
class x
{
// ctor w/ parameter
//
x(int a)
{
// logic here
}
// default ctor (contains no parameter)
//
x()
{
// logic here
}
}
Then i need to add a setter method named reset that can be used to reset the two private instance variables of this class to two values passed in as parameters.
Setter methods are used to "encapsulate" member variables by "setting" their value via public function. See here.
Example:
class x
{
private int _number;
// Setter, used to set the value of '_number'
//
public void setNumber(int value)
{
_number = value;
}
}
Then I need to add a getter method named getName and getId that can be used to retrieve these two private variables
Getters do the opposite. Instead of "setting" the value of a private member variable, they are used to "get" the value from the member variable.
Example:
class x
{
private int _number;
// Getter, used to return the value of _number
//
public int getNumber()
{
return _number;
}
}
Hope this helps
I highly recommend consulting the Java Tutorials, which should be very helpful here. For example, there is a section on constructors which details how they work, even giving an example of a no-argument form:
Although Bicycle only has one constructor, it could have others, including a no-argument constructor:
public Bicycle() {
gear = 1;
cadence = 10;
speed = 0;
}
Bicycle yourBike = new Bicycle(); invokes the no-argument constructor to create a new Bicycle object called yourBike.
Similarly, there are sections dedicated to defining methods and passing information to them. There's even a section on returning values from your method.
Read the above and you should be able to complete your homework :-)
I'm trying to create a form using Spring #MVC 3.0. This form is supposed to let the user update the flavor of a soup, but I'm running into some issues. Here are my relevant model classes (assume all the getters and setters are there):
public class Soup {
String name, flavor, mainIngredient;
public String toString()
{
return "name: " + this.name + ", flavor: "
+ this.flavor + ", main: " + this.mainIngredient;
}
}
public class SmallMeal {
List<Soup> soups;
}
public class Menu {
String id;
SmallMeal smallMeals;
}
This part of the application allows the user to edit the soups. Here's the controller:
public class MenuController {
#RequestMapping(value="/soupForm", method = RequestMethod.GET)
public String updateSoups(
#PathVariable("menuId") String menuId,
ModelMap model)
{
Menu menu = // ... looked up with a DAO.
if (menu != null)
{
model.addAttribute("menu", menu);
for (Soup s : menu.getSmallMeal().getSoups())
{
System.out.println("soup: " + s.toString());
}
}
return "soupEditor";
}
#RequestMapping(value="/soupForm", method = RequestMethod.POST)
public String updateSoups(
#ModelAttribute("menu") Menu menu,
BindingResult result, SessionStatus status)
{
for (Soup s : menu.getSmallMeal().getSoups())
{
System.out.println("soup: " + s.toString());
}
return "redirect:soupUpdated";
}
}
And here's the soupForm JSP:
<form:form modelAttribute="menu">
<c:forEach var="soup" varStatus="stat" items="${menu.smallMeal.soups}">
<label>${soup.name} flavor</label>
<form:input path="smallMeal.soups[${stat.index}].flavor" />
</c:forEach>
</form:form>
Now, when I first request the page, I get an output similar to this:
name: tortilla, flavor: spicy, mainIngredient: tortillas
name: crab bisque, flavor: crab, mainIngredient: crab
And after I submit the form (after updating the flavor for tortilla), I would get an output similar to:
name: null, flavor: delicious, mainIngredient: null
name: null, flavor: null, mainIngredient: null
What's going wrong? Why isn't the rest of the data of the object being bound?
Turns out that the #SessionAttributes annotation was the answer.
#Controller
#SessionAttributes("menu")
public class MenuController {
/* ... */
}
After that was added, the data currently existing on the object persisted.